Libstdc++-porting-howtoFelixNatterfnatter@gmx.net0.5Thu Jun 1 13:06:50 2000fnatterFirst docbook-version.0.8Sun Jul 30 20:28:40 2000fnatterFirst released version using docbook-xml
+ second upload to libstdc++-page.
0.9Wed Sep 6 02:59:32 2000fnatter5 new sections.0.9.1Sat Sep 23 14:20:15 2000fnatteradded information about why file-descriptors are not in the
standard0.9.2Tue Jun 5 20:07:49 2001fnatter
a fix, added hint on increased portability of C-shadow-headers,
added autoconf-test HAVE_CONTAINER_AT
0.9.3Fri Jun 29 16:15:56 2001fnatter
changed signature of nonstandard filebuf-constructor and
update the section on filebuf::attach to point to ../ext/howto.html,
added link to ../21/strings/howto.html
in sec-stringstream, changed <link>-tags to have content
(so that these links work),
replace "user-space" by "global namespace"
add note about gcc 3.0 and shadow-headers
add section about ostream::form and istream::scan
sec-vector-at: remove hint to modify headers
fix spelling error in sec-stringstream
0.9.4Mon Nov 5 17:01:04 2001fnatter
rewrite section 1.1.3 because of gnu.gcc.help-post by
Juergen Heinzl
Legal Notice
This document can be distributed under the FDL
(www.gnu.org)
Tue Jun 5 20:07:49 2001
Some notes on porting applications from libstdc++-2.90 (or earlier
versions) to libstdc++-v3. Not speaking in terms of the GNU libstdc++
implementations, this means porting from earlier versions of the
C++-Standard to ISO 14882.
In the following, when I say portable, I will refer to "portable among ISO
14882-implementations". On the other hand, if I say "backportable" or
"conservative", I am talking about "compiles with older
libstdc++-implementations".
Namespace std::
The latest C++-standard (ISO-14882) requires that the standard
C++-library is defined in namespace std::. Thus, in order to use
classes from the standard C++-library, you can do one of three
things:
wrap your code in namespace std {
... } => This is not an option because only symbols
from the standard c++-library are defined in namespace std::.
put a kind of
using-declaration in your source (either
using namespace std; or i.e. using
std::string;) => works well for source-files, but
cannot be used in header-files.
use a fully qualified name for
each libstdc++-symbol (i.e. std::string,
std::cout) => can always be used
Because there are many compilers which still use an implementation
that does not have the standard C++-library in namespace
std::, some care is required to support these as
well.
Namespace back-portability-issues are generally not a problem with
g++, because versions of g++ that do not have libstdc++ in
std:: use -fno-honor-std
(ignore std::, :: = std::) by
default. That is, the responsibility for enabling or disabling
std:: is on the user; the maintainer does not have
to care about it. This probably applies to some other compilers as
well.
The following sections list some possible solutions to support compilers
that cannot ignore std::.
Using namespace
composition if the project uses a separate
namespaceGtk-- defines
most of its classes in namespace Gtk::. Thus, it was possible to
adapt Gtk-- to namespace std:: by using a C++-feature called
namespace composition. This is what happens if
you put a using-declaration into a
namespace-definition: the imported symbol(s) gets imported into the
currently active namespace(s). For example:
namespace Gtk {
using std::string;
class Window { ... }
}
In this example, std::string gets imported into
namespace Gtk::. The result is that you don't have to use
std::string in this header, but still
std::string does not get imported into
the global namespace (::) unless the user does
using namespace Gtk; (which is not recommended
practice for Gtk--, so it is not a problem). Additionally, the
using-declarations are wrapped in macros that
are set based on autoconf-tests to either "" or i.e. using
std::string; (depending on whether the system has
libstdc++ in std:: or not). (ideas from
llewelly@dbritsch.dsl.xmission.com, Karl Nelson
kenelson@ece.ucdavis.edu)
Defining an empty namespace std
By defining an (empty) namespace std:: before
using it, you avoid getting errors on systems where no part of the
library is in namespace std:
namespace std { }
using namespace std;
Avoid to use fully qualified names
(i.e. std::string)
If some compilers complain about using
std::string;, and if the "hack" for gtk-- mentioned above
does not work, then I see two solutions:
Define std:: as a macro if the compiler
doesn't know about std::.
#ifdef OLD_COMPILER
#define std
#endif
(thanks to Juergen Heinzl who posted this solution on
gnu.gcc.help)
Define a macro NS_STD, which is defined to
either "" or "std"
based on an autoconf-test. Then you should be able to use
NS_STD::string, which will evaluate to
::string ("string in the global namespace") on
systems that do not put string in std::. (This is untested)
How some open-source-projects deal
with this
This information was gathered around May 2000. It may not be correct
by the time you read this.
Namespace std:: in Open-Source programsclanlibusualpingususualmozillausualmnemonicnone
libsigc++conservative-impl
Notations for categoriesusualmostly fully qualified names and some
using-declarations (but not in headers)noneno namespace std at allconservative-implwrap all
namespace-handling in macros to support compilers without
namespace-support (no libstdc++ used in headers)
As you can see, this currently lacks an example of a project
which uses libstdc++-symbols in headers in a back-portable way
(except for Gtk--: see the section on the gtkmm-hack).
there is no ios::nocreate/ios::noreplace
in ISO 14882
I have seen ios::nocreate being used for
input-streams, most probably because the author thought it would be
more correct to specify nocreate "explicitly". So you can simply
leave it out for input-streams.
For output streams, "nocreate" is probably the default, unless you
specify std::ios::trunc ? To be safe, you can open
the file for reading, check if it has been opened, and then decide
whether you want to create/replace or not. To my knowledge, even
older implementations support app,
ate and trunc (except for
app ?).
stream::attach(int
fd) is not in the standard any more
Phil Edwards pedwards@disaster.jaj.com writes:
It was considered and rejected. Not all environments use file
descriptors. Of those that do, not all of them use integers to represent
them.
When using libstdc++-v3, you can use
#include <fstream>
basic_filebuf<...>::basic_filebuf<...>
__c_file_type* fileios_base::open_mode modeint size
but the the signature of this constructor has changed often, and
it might change again. For the current state of this, check
the howto for extensions.
For a portable solution (among systems which use
filedescriptors), you need to implement a subclass of
std::streambuf (or
std::basic_streambuf<..>) which opens a file
given a descriptor, and then pass an instance of this to the
stream-constructor. For an example of this, refer to
fdstream example
by Nicolai Josuttis.
The new headers
All new headers can be seen in this
source-code.
The old C++-headers (iostream.h etc.) are available, but gcc generates
a warning that you are using deprecated headers.
New headers replacing C-headers
You should not use the C-headers (except for system-level
headers) from C++ programs. Instead, you should use a set of
headers that are named by prepending 'c' and, as usual,
omitting the extension (.h). For example, instead of using
<math.h>, you
should use <cmath>. In some cases this has
the advantage that the C++-header is more standardized than
the C-header (i.e. <ctime> (almost)
corresponds to either <time.h> or <sys/time.h>).
The standard specifies that if you include the C-style header
(<math.h> in
this case), the symbols will be available both in the global
namespace and in namespace std:: (but
libstdc++ does not yet have fully compliant headers) On the
other hand, if you include only the new header (i.e. <cmath>), the symbols
will only be defined in namespace std::
(and macros will be converted to inline-functions).
For more information on this, and for information on how the
GNU C++ implementation might reuse ("shadow") the C
library-functions, have a look at
www.cantrip.org.
<fstream> does
not define std::cout,
std::cin etc.
In earlier versions of the standard,
<fstream.h>,
<ostream.h>
and <istream.h>
used to define
cout, cin and so on. Because
of the templatized iostreams in libstdc++-v3, you need to include
<iostream>
explicitly to define these.
Iterators
The following are not proper uses of iterators, but may be working
fixes for existing uses of iterators.
you cannot do
ostream::operator<<(iterator) to
print the address of the iterator => use
operator<< &*iterator instead ?
you cannot clear an iterator's reference
(iterator = 0) => use
iterator = iterator_type(); ?
if (iterator) won't work any
more => use if (iterator != iterator_type())
?
Libc-macros (i.e. isspace from
<cctype>)
Glibc 2.0.x and 2.1.x define the
<ctype.h>
-functionality as macros (isspace, isalpha etc.). Libstdc++-v3
"shadows" these macros as described in the section about
c-headers.
Older implementations of libstdc++ (g++-2 for egcs 1.x and g++-3
for gcc 2.95.x), however, keep these functions as macros, and so it
is not back-portable to use fully qualified names. For example:
#include <cctype>
int main() { std::isspace('X'); }
will result in something like this (unless using g++-v3):
std:: (__ctype_b[(int) ( ( 'X' ) )] & (unsigned short int)
_ISspace ) ;
One solution I can think of is to test for -v3 using
autoconf-macros, and define macros for each of the C-functions
(maybe that is possible with one "wrapper" macro as well ?).
Another solution which would fix g++ is to tell the user to modify a
header-file so that g++-2 (egcs 1.x) and g++-3 (gcc 2.95.x) define a
macro which tells <ctype.h> to define functions
instead of macros:
// This keeps isalnum, et al from being propagated as macros.
#if __linux__
#define __NO_CTYPE 1
#endif
[ now include <ctype.h> ]
Another problem arises if you put a using namespace
std; declaration at the top, and include <ctype.h>. This will result in
ambiguities between the definitions in the global namespace
(<ctype.h>) and the
definitions in namespace std::
(<cctype>).
The solution to this problem was posted to the libstdc++-v3
mailing-list:
Benjamin Kosnik bkoz@redhat.com writes:
--enable-cshadow-headers is currently broken. As a result, shadow
headers are not being searched....
This is now outdated, but gcc 3.0 still does not have fully
compliant "shadow headers".
State of streams
At least some older implementations don't have
std::ios_base, so you should use
std::ios::badbit, std::ios::failbit
and std::ios::eofbit and
std::ios::goodbit.
vector::at is missing (i.e. gcc 2.95.x)
One solution is to add an autoconf-test for this:
AC_MSG_CHECKING(for container::at)
AC_TRY_COMPILE(
[
#include <vector>
#include <deque>
#include <string>
using namespace std;
],
[
deque<int> test_deque(3);
test_deque.at(2);
vector<int> test_vector(2);
test_vector.at(1);
string test_string("test_string");
test_string.at(3);
],
[AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_CONTAINER_AT)],
[AC_MSG_RESULT(no)])
If you are using other (non-GNU) compilers it might be a good idea
to check for string::at separately.
Using std::char_traits<char>::eof()
#ifdef HAVE_CHAR_TRAITS
#define CPP_EOF std::char_traits<char>::eof()
#else
#define CPP_EOF EOF
#endif
Using string::clear()/string::erase()
There are two functions for deleting the contents of a string:
clear and erase (the latter
returns the string).
void
clear() { _M_mutate(0, this->size(), 0); }
basic_string&
erase(size_type __pos = 0, size_type __n = npos)
{
return this->replace(_M_check(__pos), _M_fold(__pos, __n),
_M_data(), _M_data());
}
The implementation of erase seems to be more
complicated (from libstdc++-v3), but clear is not
implemented in gcc 2.95.x's libstdc++, so you should use
erase (which is probably faster than
operator=(charT*)).
GNU Extensions ostream::form and istream::scan
These are not supported any more - use
stringstreams instead.
Using stringstreams
Libstdc++-v3 provides the new
i/ostringstream-classes, (<sstream>), but for compatibility
with older implementations you still have to use
i/ostrstream (<strstream>):
#ifdef HAVE_SSTREAM
#include <sstream>
#else
#include <strstream>
#endif
strstream is considered to be
deprecated
strstream is limited to
char with ostringstream you don't
have to take care of terminating the string or freeing its
memory
istringstream can be re-filled
(clear(); str(input);)
You can then use output-stringstreams like this:
#ifdef HAVE_SSTREAM
std::ostringstream oss;
#else
std::ostrstream oss;
#endif
oss << "Name=" << m_name << ", number=" << m_number << std::endl;
...
#ifndef HAVE_SSTREAM
oss << std::ends; // terminate the char*-string
#endif
// str() returns char* for ostrstream and a string for ostringstream
// this also causes ostrstream to think that the buffer's memory
// is yours
m_label.set_text(oss.str());
#ifndef HAVE_SSTREAM
// let the ostrstream take care of freeing the memory
oss.freeze(false);
#endif
Input-stringstreams can be used similarly:
std::string input;
...
#ifdef HAVE_SSTREAM
std::istringstream iss(input);
#else
std::istrstream iss(input.c_str());
#endif
int i;
iss >> i;
One (the only?) restriction is that an istrstream cannot be re-filled:
std::istringstream iss(numerator);
iss >> m_num;
// this is not possible with istrstream
iss.clear();
iss.str(denominator);
iss >> m_den;
If you don't care about speed, you can put these conversions in
a template-function:
template <class X>
void fromString(const string& input, X& any)
{
#ifdef HAVE_SSTREAM
std::istringstream iss(input);
#else
std::istrstream iss(input.c_str());
#endif
X temp;
iss >> temp;
if (iss.fail())
throw runtime_error(..)
any = temp;
}
Another example of using stringstreams is in this howto.
I have read the Josuttis book on Standard C++, so some information
comes from there. Additionally, there is information in
"info iostream", which covers the old implementation that gcc 2.95.x
uses.
About...
Please send any experience, additions, corrections or questions to
fnatter@gmx.net or for
discussion to the libstdc++-v3-mailing-list.