Libstdc++-porting-howto
Felix Natter
Legal Notice
This document can be distributed under the FDL
(
http://www.gnu.org
www.gnu.org
)
Tue Jun  5 20:07:49 2001
Revision History
Revision 0.5
Thu Jun  1 13:06:50 2000
fnatter
First docbook-version.
Revision 0.8
Sun Jul 30 20:28:40 2000
fnatter
First released version using docbook-xml
+ second upload to libstdc++-page.
Revision 0.9
Wed Sep  6 02:59:32 2000
fnatter
5 new sections.
Revision 0.9.1
Sat Sep 23 14:20:15 2000
fnatter
added information about why file-descriptors are not in the
standard
Revision 0.9.2
Tue Jun  5 20:07:49 2001
fnatter
a fix, added hint on increased portability of C-shadow-headers,
added autoconf-test HAVE_CONTAINER_AT
Revision 0.9.3
Fri Jun 29 16:15:56 2001
fnatter
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
Revision 0.9.4
Mon Nov  5 17:01:04 2001
fnatter
rewrite section 1.1.3 because of gnu.gcc.help-post by
Juergen Heinzl
Abstract
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.
Table of Contents
1.
#sec-nsstd
Namespace std::
1.1.1.
#sec-gtkmm-hack
Using namespace
composition if the project uses a separate
namespace
1.1.2.
#sec-emptyns
Defining an empty namespace std
1.1.3.
#sec-avoidfqn
Avoid to use fully qualified names
(i.e. std::string)
1.1.4.
#sec-osprojects
How some open-source-projects deal
with this
2.
#sec-nocreate
there is no ios::nocreate/ios::noreplace
in ISO 14882
3.
#sec-stream::attach
stream::attach(int
fd) is not in the standard any more
4.
#sec-headers
The new headers
4.4.1.
#sec-cheaders
New headers replacing C-headers
4.4.2.
#sec-fstream-header
<fstream> does
not define std::cout,
std::cin etc.
5.
#sec-iterators
Iterators
6.
#sec-macros
Libc-macros (i.e. isspace from
<cctype>)
7.
#sec-stream-state
State of streams
8.
#sec-vector-at
vector::at is missing (i.e. gcc 2.95.x)
9.
#sec-eof
Using std::char_traits<char>::eof()
10.
#sec-string-clear
Using string::clear()/string::erase()
11.
#sec-scan-form
GNU Extensions ostream::form and istream::scan
12.
#sec-stringstream
Using stringstreams
13.
#sec-about
About...
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
namespace
http://gtkmm.sourceforge.net
Gtk--
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
<
mailto:llewelly@dbritsch.dsl.xmission.com
llewelly@dbritsch.dsl.xmission.com
>
, Karl Nelson
<
mailto:kenelson@ece.ucdavis.edu
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.
Table 1. Namespace std:: in Open-Source programs
http://www.clanlib.org
clanlib
usual
http://pingus.seul.org
pingus
usual
http://www.mozilla.org
mozilla
usual
http://libsigc.sourceforge.net
libsigc++
conservative-impl
Table 2. Notations for categories
usual
mostly fully qualified names and some
using-declarations (but not in headers)
none
no namespace std at all
conservative-impl
wrap 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
#sec-gtkmm-hack
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
<
mailto:pedwards@disaster.jaj.com
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<...>
(
file
,
mode
,
size
);
__c_file_type*
file
;
ios_base::open_mode
mode
;
int
size
;
but the the signature of this constructor has changed often, and
it might change again. For the current state of this, check
../ext/howto.html
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
http://www.josuttis.com/cppcode/fdstream.html
fdstream example
by Nicolai Josuttis.
The new headers
All new headers can be seen in this
headers_cc.txt
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
http://www.cantrip.org/cheaders.html
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
#sec-cheaders
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
<
mailto:bkoz@redhat.com
bkoz@redhat.com
>
writes:
&##x2018;
--enable-cshadow-headers is currently broken. As a result, shadow
headers are not being searched....
&##x2019;
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
#sec-stringstream
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
../21_strings/howto.html
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
mailto:fnatter@gmx.net
fnatter@gmx.net
or for
discussion to the libstdc++-v3-mailing-list.
