Commit 89cd2243 by Felix Natter Committed by Phil Edwards

porting-howto.xml: Initial checkin of master copy.

2001-07-11   Felix Natter  <fnatter@gmx.net>

	* docs/html/17_intro/porting-howto.xml:  Initial checkin of
	master copy.
	* docs/html/17_intro/porting-howto.html:  check in v0.9.3

From-SVN: r43953
parent 7f905405
2001-07-11 Felix Natter <fnatter@gmx.net>
* docs/html/17_intro/porting-howto.xml: Initial checkin of
master copy.
* docs/html/17_intro/porting-howto.html: check in v0.9.3
2001-07-11 Phil Edwards <pme@sources.redhat.com>
* docs/doxygen/run_doxygen: Don't keep output from previous run.
......
<!DOCTYPE HTML PUBLIC "http://www.w3.org/TR/REC-html40/loose.dtd" "-//W3C//DTD HTML 4.0 Transitional//EN">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<meta content="text/html; charset=ISO-8859-1" http-equiv="Content-Type">
<title>Libstdc++-porting-howto</title>
<meta name="generator" content="DocBook XSL Stylesheets V1.29">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div id="libstdc++-porting" class="article">
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div id="libstdc++-porting-howto" class="article">
<div class="titlepage">
<div><h1 class="title">
<a name="libstdc++-porting"></a>Libstdc++-porting-howto</h1></div>
<a name="libstdc++-porting-howto"></a>Libstdc++-porting-howto</h1></div>
<div><h3 class="author">Felix Natter</h3></div>
<div><div class="legalnotice">
<p class="legalnotice-title"><b>Legal Notice</b></p>
<p>
This document can be distributed under the FDL
(<a href="http://www.gnu.org" target="_top">www.gnu.org</a>)
This document can be distributed under the FDL
(<a href="http://www.gnu.org" target="_top">www.gnu.org</a>)
</p>
</div></div>
<div><p class="pubdate">Tue Jun 5 20:07:49 2001</p></div>
......@@ -32,8 +32,8 @@
<td align="left">fnatter</td>
</tr>
<tr><td align="left" colspan="3">First released version using docbook-xml
+ second upload to libstdc++-page.
</td></tr>
+ second upload to libstdc++-page.
</td></tr>
<tr>
<td align="left">Revision 0.9</td>
<td align="left">Wed Sep 6 02:59:32 2000</td>
......@@ -46,21 +46,37 @@
<td align="left">fnatter</td>
</tr>
<tr><td align="left" colspan="3">added information about why file-descriptors are not in the
standard</td></tr>
standard</td></tr>
<tr>
<td align="left">Revision 0.9.2</td>
<td align="left">Tue Jun 5 20:07:49 2001</td>
<td align="left">fnatter</td>
</tr>
<tr><td align="left" colspan="3">
a fix, added hint on increased portability of C-shadow-headers,
added autoconf-test HAVE_CONTAINER_AT
</td></tr>
a fix, added hint on increased portability of C-shadow-headers,
added autoconf-test HAVE_CONTAINER_AT
</td></tr>
<tr>
<td align="left">Revision 0.9.3</td>
<td align="left">Fri Jun 29 16:15:56 2001</td>
<td align="left">fnatter</td>
</tr>
<tr><td align="left" colspan="3">
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 &lt;link&gt;-tags to have content
(so that these links work),
replace &quot;user-space&quot; by &quot;global namespace&quot;
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
</td></tr>
</table></div></div>
<div><div class="abstract">
<p>
<a name="id2715202"></a>
<b>Abstract</b>
<a name="id2695641"></a><b>Abstract</b>
</p>
<p>
Some notes on porting applications from libstdc++-2.90 (or earlier
......@@ -93,8 +109,7 @@
<dt>2. <a href="#sec-nocreate">there is no ios::nocreate/ios::noreplace
in ISO 14882</a>
</dt>
<dt>3. <a href="#sec-stream::attach">
<b>stream::attach(int
<dt>3. <a href="#sec-stream::attach"><b>stream::attach(int
fd)</b> is not in the standard any more</a>
</dt>
<dt>4. <a href="#sec-headers">The new headers</a>
......@@ -114,9 +129,7 @@
Libc-macros (i.e. <b>isspace</b> from
<tt>&lt;cctype&gt;</tt>)</a>
</dt>
<dt>7. <a href="#sec-stream-state">
State of streams
</a>
<dt>7. <a href="#sec-stream-state">State of streams</a>
</dt>
<dt>8. <a href="#sec-vector-at">vector::at is missing (i.e. gcc 2.95.x)</a>
</dt>
......@@ -124,9 +137,11 @@
</dt>
<dt>10. <a href="#sec-string-clear">Using string::clear()/string::erase()</a>
</dt>
<dt>11. <a href="#sec-stringstream">Using stringstream's</a>
<dt>11. <a href="#sec-scan-form">GNU Extensions ostream::form and istream::scan</a>
</dt>
<dt>12. <a href="#sec-stringstream">Using stringstreams</a>
</dt>
<dt>12. <a href="#sec-about">About...</a>
<dt>13. <a href="#sec-about">About...</a>
</dt>
</dl>
</div>
......@@ -137,10 +152,8 @@
libstdc++-implementations&quot;.
</p>
<div class="section">
<a name="sec-nsstd"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-nsstd"></a>
<span class="label">1.</span> <span class="title">Namespace std::</span>
<a name="sec-nsstd"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-nsstd"></a><span class="label">1.</span> <span class="title">Namespace std::</span>
</h2></div></div>
<p>
The latest C++-standard (ISO-14882) requires that the standard
......@@ -149,19 +162,19 @@
things:
<div class="itemizedlist"><ul>
<li><p>
<a name="id2715113"></a>wrap your code in <b>namespace std {
<a name="id2695691"></a>wrap your code in <b>namespace std {
... }</b> =&gt; This is not an option because only symbols
from the standard c++-library are defined in namespace std::.
</p></li>
<li><p>
<a name="id2668711"></a>put a kind of
<a name="id2695698"></a>put a kind of
<i>using-declaration</i> in your source (either
<b>using namespace std;</b> or i.e. <b>using
std::string;</b>) =&gt; works well for source-files, but
cannot be used in header-files.
</p></li>
<li><p>
<a name="id2668731"></a>use a <i>fully qualified name</i> for
<a name="id2695717"></a>use a <i>fully qualified name</i> for
each libstdc++-symbol (i.e. <b>std::string</b>,
<b>std::cout</b>) =&gt; can always be used
</p></li>
......@@ -188,10 +201,8 @@
that cannot ignore std::.
</p>
<div class="section">
<a name="sec-gtkmm-hack"></a>
<div class="titlepage"><div><h3 class="title">
<a name="sec-gtkmm-hack"></a>
<span class="label">1.1.1.</span> <span class="title">Using <i>namespace
<a name="sec-gtkmm-hack"></a><div class="titlepage"><div><h3 class="title">
<a name="sec-gtkmm-hack"></a><span class="label">1.1.1.</span> <span class="title">Using <i>namespace
composition</i> if the project uses a separate
namespace</span>
</h3></div></div>
......@@ -213,7 +224,7 @@
namespace Gtk::. The result is that you don't have to use
<b>std::string</b> in this header, but still
<b>std::string</b> does not get imported into
user-space (the global namespace ::) unless the user does
the global namespace (::) unless the user does
<b>using namespace Gtk;</b> (which is not recommended
practice for Gtk--, so it is not a problem). Additionally, the
<b>using</b>-declarations are wrapped in macros that
......@@ -225,10 +236,8 @@
</p>
</div>
<div class="section">
<a name="sec-emptyns"></a>
<div class="titlepage"><div><h3 class="title">
<a name="sec-emptyns"></a>
<span class="label">1.1.2.</span> <span class="title">Defining an empty namespace std</span>
<a name="sec-emptyns"></a><div class="titlepage"><div><h3 class="title">
<a name="sec-emptyns"></a><span class="label">1.1.2.</span> <span class="title">Defining an empty namespace std</span>
</h3></div></div>
<p>
By defining an (empty) namespace <b>std::</b> before
......@@ -241,10 +250,8 @@
</p>
</div>
<div class="section">
<a name="sec-avoidfqn"></a>
<div class="titlepage"><div><h3 class="title">
<a name="sec-avoidfqn"></a>
<span class="label">1.1.3.</span> <span class="title">Avoid to use fully qualified names
<a name="sec-avoidfqn"></a><div class="titlepage"><div><h3 class="title">
<a name="sec-avoidfqn"></a><span class="label">1.1.3.</span> <span class="title">Avoid to use fully qualified names
(i.e. std::string)</span>
</h3></div></div>
<p>
......@@ -259,10 +266,8 @@
</p>
</div>
<div class="section">
<a name="sec-osprojects"></a>
<div class="titlepage"><div><h3 class="title">
<a name="sec-osprojects"></a>
<span class="label">1.1.4.</span> <span class="title">How some open-source-projects deal
<a name="sec-osprojects"></a><div class="titlepage"><div><h3 class="title">
<a name="sec-osprojects"></a><span class="label">1.1.4.</span> <span class="title">How some open-source-projects deal
with this</span>
</h3></div></div>
<p>
......@@ -271,8 +276,7 @@
</p>
<div class="table">
<p>
<a name="id2668681"></a>
<b>Table 1. Namespace std:: in Open-Source programs</b>
<a name="id2696268"></a><b>Table 1. Namespace std:: in Open-Source programs</b>
</p>
<table summary="Namespace std:: in Open-Source programs" border="1">
<colgroup>
......@@ -306,8 +310,7 @@
</div>
<div class="table">
<p>
<a name="id2666288"></a>
<b>Table 2. Notations for categories</b>
<a name="id2696334"></a><b>Table 2. Notations for categories</b>
</p>
<table summary="Notations for categories" border="1">
<colgroup>
......@@ -334,17 +337,15 @@
</table>
</div>
<p>
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 <a href="#sec-gtkmm-hack"></a>).
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 <a href="#sec-gtkmm-hack">section on the gtkmm-hack</a>).
</p>
</div>
</div>
<div class="section">
<a name="sec-nocreate"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-nocreate"></a>
<span class="label">2.</span> <span class="title">there is no ios::nocreate/ios::noreplace
<a name="sec-nocreate"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-nocreate"></a><span class="label">2.</span> <span class="title">there is no ios::nocreate/ios::noreplace
in ISO 14882</span>
</h2></div></div>
<p>
......@@ -364,11 +365,8 @@
</p>
</div>
<div class="section">
<a name="sec-stream::attach"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-stream::attach"></a>
<span class="label">3.</span> <span class="title">
<b>stream::attach(int
<a name="sec-stream::attach"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-stream::attach"></a><span class="label">3.</span> <span class="title"><b>stream::attach(int
fd)</b> is not in the standard any more</span>
</h2></div></div>
<p>
......@@ -379,32 +377,33 @@
</p>
<p>
When using libstdc++-v3, you can use
<div class="funcsynopsis" id="id2666593">
<div class="funcsynopsis" id="id2696909">
<p>
<a name="id2666593"></a>
<pre class="funcsynopsisinfo">
<a name="id2696909"></a><pre class="funcsynopsisinfo">
#include &lt;fstream&gt;
</pre>
<p><code>
<code class="funcdef">int <b class="fsfunc">basic_filebuf</b>
</code>(<var class="pdparam">__fd</var>, <var class="pdparam">__name</var>, <var class="pdparam">__mode</var>);<br>int <var class="pdparam">__fd</var>;<br>const char* <var class="pdparam">__name</var>;<br>ios_base::openmode <var class="pdparam">__mode</var>;</code></p>
<p><code><code class="funcdef">
<b class="fsfunc">basic_filebuf&lt;...&gt;::basic_filebuf&lt;...&gt;
</b>
</code>(<var class="pdparam">file</var>, <var class="pdparam">mode</var>, <var class="pdparam">size</var>);<br>__c_file_type* <var class="pdparam">file</var>;<br>ios_base::open_mode <var class="pdparam">mode</var>;<br>int <var class="pdparam">size</var>;</code></p>
<p>
</div>
but the the signature of this constructor has changed often, and
it might change again.
it might change again. For the current state of this, check
<a href="../ext/howto.html" target="_top">the howto for extensions</a>.
</p>
<p>
For a portable solution (among systems which use
filedescriptors), you need to implement a subclass of
<b>streambuf</b> (or
<b>basic_streambuf&lt;..&gt;</b>) which opens a file
<b>std::streambuf</b> (or
<b>std::basic_streambuf&lt;..&gt;</b>) which opens a file
given a descriptor, and then pass an instance of this to the
stream-constructor (from the Josuttis-book).
</p>
</div>
<div class="section">
<a name="sec-headers"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-headers"></a>
<span class="label">4.</span> <span class="title">The new headers</span>
<a name="sec-headers"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-headers"></a><span class="label">4.</span> <span class="title">The new headers</span>
</h2></div></div>
<p>
All new headers can be seen in this <a href="headers_cc.txt" target="_top">
......@@ -415,10 +414,8 @@
a warning that you are using deprecated headers.
</p>
<div class="section">
<a name="sec-cheaders"></a>
<div class="titlepage"><div><h3 class="title">
<a name="sec-cheaders"></a>
<span class="label">4.4.1.</span> <span class="title">New headers replacing C-headers</span>
<a name="sec-cheaders"></a><div class="titlepage"><div><h3 class="title">
<a name="sec-cheaders"></a><span class="label">4.4.1.</span> <span class="title">New headers replacing C-headers</span>
</h3></div></div>
<p>
You should not use the C-headers (except for system-level
......@@ -448,10 +445,8 @@
</p>
</div>
<div class="section">
<a name="sec-fstream-header"></a>
<div class="titlepage"><div><h3 class="title">
<a name="sec-fstream-header"></a>
<span class="label">4.4.2.</span> <span class="title">
<a name="sec-fstream-header"></a><div class="titlepage"><div><h3 class="title">
<a name="sec-fstream-header"></a><span class="label">4.4.2.</span> <span class="title">
<tt>&lt;fstream&gt;</tt> does
not define <b>std::cout</b>,
<b>std::cin</b> etc.</span>
......@@ -470,46 +465,43 @@
</div>
</div>
<div class="section">
<a name="sec-iterators"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-iterators"></a>
<span class="label">5.</span> <span class="title">Iterators</span>
<a name="sec-iterators"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-iterators"></a><span class="label">5.</span> <span class="title">Iterators</span>
</h2></div></div>
<p>
The following are not proper uses of iterators, but may be working
fixes for existing uses of iterators.
<div class="itemizedlist"><ul>
<li><p>
<a name="id2664787"></a>you cannot do
<a name="id2692127"></a>you cannot do
<b>ostream::operator&lt;&lt;(iterator)</b> to
print the address of the iterator =&gt; use
<b>operator&lt;&lt; &amp;*iterator</b> instead ?
</p></li>
<li><p>
<a name="id2664753"></a>you cannot clear an iterator's reference
<a name="id2697070"></a>you cannot clear an iterator's reference
(<b>iterator = 0</b>) =&gt; use
<b>iterator = iterator_type();</b> ?
</p></li>
<li><p>
<a name="id2664806"></a>
<b>if (iterator)</b> won't work any
<a name="id2697221"></a><b>if (iterator)</b> won't work any
more =&gt; use <b>if (iterator != iterator_type())</b>
?</p></li>
</ul></div>
</p>
</div>
<div class="section">
<a name="sec-macros"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-macros"></a>
<span class="label">6.</span> <span class="title">
<a name="sec-macros"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-macros"></a><span class="label">6.</span> <span class="title">
Libc-macros (i.e. <b>isspace</b> from
<tt>&lt;cctype&gt;</tt>)</span>
</h2></div></div>
<p>
Glibc 2.0.x and 2.1.x define the <tt>&lt;ctype.h&gt;</tt> -functionality as
macros (isspace, isalpha etc.). Libstdc++-v3 &quot;shadows&quot; these macros
as described in the <a href="#sec-cheaders"></a>.
Glibc 2.0.x and 2.1.x define the
<tt>&lt;ctype.h&gt;</tt>
-functionality as macros (isspace, isalpha etc.). Libstdc++-v3
&quot;shadows&quot; these macros as described in the <a href="#sec-cheaders">section about
c-headers</a>.
</p>
<p>
Older implementations of libstdc++ (g++-2 for egcs 1.x and g++-3
......@@ -560,15 +552,13 @@
--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 &quot;shadow headers&quot;.
</p>
</div>
<div class="section">
<a name="sec-stream-state"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-stream-state"></a>
<span class="label">7.</span> <span class="title">
State of streams
</span>
<a name="sec-stream-state"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-stream-state"></a><span class="label">7.</span> <span class="title">State of streams</span>
</h2></div></div>
<p>
At least some older implementations don't have
......@@ -579,31 +569,11 @@
</p>
</div>
<div class="section">
<a name="sec-vector-at"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-vector-at"></a>
<span class="label">8.</span> <span class="title">vector::at is missing (i.e. gcc 2.95.x)</span>
<a name="sec-vector-at"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-vector-at"></a><span class="label">8.</span> <span class="title">vector::at is missing (i.e. gcc 2.95.x)</span>
</h2></div></div>
<p>
You could easily modify the header-files:
<tt>prefix/include/g++-3/stl_vector.h</tt>:
<pre class="programlisting">
reference operator[](size_type __n) { return *(begin() + __n); }
reference at(size_type __n) {
if (begin() + __n &gt;= end())
throw out_of_range(&quot;vector::at&quot;);
return *(begin() + __n);
}
const_reference operator[](size_type __n) const { return *(begin() + __n); }
const_reference at(size_type __n) const {
if (begin() + __n &gt;= end())
throw out_of_range(&quot;vector::at&quot;);
return *(begin() + __n);
}
</pre>
(and so on for <b>deque</b> and <b>string</b>)
Of course a better solution is to add an autoconf-test for this:
One solution is to add an autoconf-test for this:
<pre class="programlisting">
AC_MSG_CHECKING(for container::at)
AC_TRY_COMPILE(
......@@ -631,10 +601,8 @@
</p>
</div>
<div class="section">
<a name="sec-eof"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-eof"></a>
<span class="label">9.</span> <span class="title">Using std::char_traits&lt;char&gt;::eof()</span>
<a name="sec-eof"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-eof"></a><span class="label">9.</span> <span class="title">Using std::char_traits&lt;char&gt;::eof()</span>
</h2></div></div>
<p>
<pre class="programlisting">
......@@ -647,10 +615,8 @@
</p>
</div>
<div class="section">
<a name="sec-string-clear"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-string-clear"></a>
<span class="label">10.</span> <span class="title">Using string::clear()/string::erase()</span>
<a name="sec-string-clear"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-string-clear"></a><span class="label">10.</span> <span class="title">Using string::clear()/string::erase()</span>
</h2></div></div>
<p>
There are two functions for deleting the contents of a string:
......@@ -676,17 +642,24 @@
</p>
</div>
<div class="section">
<a name="sec-stringstream"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-stringstream"></a>
<span class="label">11.</span> <span class="title">Using stringstream's</span>
<a name="sec-scan-form"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-scan-form"></a><span class="label">11.</span> <span class="title">GNU Extensions ostream::form and istream::scan</span>
</h2></div></div>
<p>
These are not supported any more - use
<a href="#sec-stringstream">
stringstreams</a> instead.
</p>
</div>
<div class="section">
<a name="sec-stringstream"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-stringstream"></a><span class="label">12.</span> <span class="title">Using stringstreams</span>
</h2></div></div>
<p>
Libstdc++-v3 provides the new
<b>i/ostringstream</b>-classes,
(<tt>&lt;sstream&gt;</tt>), but with older
implementations you still have to use <b>i/ostrstream</b>
(<tt>&lt;strstream&gt;</tt>):
<b>i/ostringstream</b>-classes, (<tt>&lt;sstream&gt;</tt>), but for compatibility
with older implementations you still have to use
<b>i/ostrstream</b> (<tt>&lt;strstream&gt;</tt>):
<pre class="programlisting">
#ifdef HAVE_SSTREAM
#include &lt;sstream&gt;
......@@ -696,20 +669,20 @@
</pre>
<div class="itemizedlist"><ul>
<li><p>
<a name="id2665237"></a> <b>strstream</b> is considered to be
<a name="id2692504"></a> <b>strstream</b> is considered to be
deprecated
</p></li>
<li><p>
<a name="id2665256"></a> <b>strstream</b> is limited to
<a name="id2692452"></a> <b>strstream</b> is limited to
<b>char</b>
</p></li>
<li><p>
<a name="id2665271"></a> with <b>ostringstream</b> you don't
<a name="id2692539"></a> with <b>ostringstream</b> you don't
have to take care of terminating the string or freeing its
memory
</p></li>
<li><p>
<a name="id2665284"></a> <b>istringstream</b> can be re-filled
<a name="id2692552"></a> <b>istringstream</b> can be re-filled
(clear(); str(input);)
</p></li>
</ul></div>
......@@ -766,17 +739,18 @@
void fromString(const string&amp; input, X&amp; any)
{
#ifdef HAVE_SSTREAM
std::istringstream iss(input);
std::istringstream iss(input);
#else
std::istrstream iss(input.c_str());
std::istrstream iss(input.c_str());
#endif
X temp;
iss &gt;&gt; temp;
if (iss.fail())
throw runtime_error(..)
any = temp;
X temp;
iss &gt;&gt; temp;
if (iss.fail())
throw runtime_error(..)
any = temp;
}
</pre>
Another example of using stringstreams is in <a href="../21_strings/howto.html" target="_top">this howto</a>.
</p>
<p>
I have read the Josuttis book on Standard C++, so some information
......@@ -786,10 +760,8 @@
</p>
</div>
<div class="section">
<a name="sec-about"></a>
<div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-about"></a>
<span class="label">12.</span> <span class="title">About...</span>
<a name="sec-about"></a><div class="titlepage"><div><h2 class="title" style="clear: all">
<a name="sec-about"></a><span class="label">13.</span> <span class="title">About...</span>
</h2></div></div>
<p>
Please send any experience, additions, corrections or questions to
......
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1//EN"
"dtd/docbook-4/docbookx.dtd">
<?xml-stylesheet type="text/xsl" href="docbook-xslt/docbook/html/docbook.xsl"?>
<!--
This is written using docbook 4.1 xml. HTML is generated using
the xslt-stylesheets from http://www.nwalsh.com.
xsltproc is an xslt-processor included in libxslt:
(http://xmlsoft.org/XSLT/ or here:
ftp://ftp.gnome.org/pub/GNOME/unstable/sources/libxslt/)
(it requires libxml2: http://xmlsoft.org
or here: ftp://ftp.gnome.org/pub/GNOME/stable/sources/libxml/)
You can find the latest version of this document here:
http://gcc.gnu.org/onlinedocs/libstdc++/17_intro/porting-howto(.html|.xml)
-->
<!-- TODO:
o remove //@label: use automatic numbering
o make this work: <link linkend="sec-gtkmm-hack" endterm="sec-gtkmm-hack.title"/>.
-->
<article class = "whitepaper" id = "libstdc++-porting-howto" lang = "en">
<articleinfo>
<title>Libstdc++-porting-howto</title>
<author>
<firstname>Felix</firstname>
<surname>Natter</surname>
</author>
<address>
<email>fnatter@gmx.net</email>
</address>
<revhistory>
<revision>
<revnumber>0.5</revnumber>
<date>Thu Jun 1 13:06:50 2000</date>
<authorinitials>fnatter</authorinitials>
<revremark>First docbook-version.</revremark>
</revision>
<revision>
<revnumber>0.8</revnumber>
<date>Sun Jul 30 20:28:40 2000</date>
<authorinitials>fnatter</authorinitials>
<revremark>First released version using docbook-xml
+ second upload to libstdc++-page.
</revremark>
</revision>
<revision>
<revnumber>0.9</revnumber>
<date>Wed Sep 6 02:59:32 2000</date>
<authorinitials>fnatter</authorinitials>
<revremark>5 new sections.</revremark>
</revision>
<revision>
<revnumber>0.9.1</revnumber>
<date>Sat Sep 23 14:20:15 2000</date>
<authorinitials>fnatter</authorinitials>
<revremark>added information about why file-descriptors are not in the
standard</revremark>
</revision>
<revision>
<revnumber>0.9.2</revnumber>
<date>Tue Jun 5 20:07:49 2001</date>
<authorinitials>fnatter</authorinitials>
<revremark>
a fix, added hint on increased portability of C-shadow-headers,
added autoconf-test HAVE_CONTAINER_AT
</revremark>
</revision>
<revision>
<revnumber>0.9.3</revnumber>
<date>Fri Jun 29 16:15:56 2001</date>
<authorinitials>fnatter</authorinitials>
<revremark>
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 &lt;link&gt;-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
</revremark>
</revision>
</revhistory>
<legalnotice><title>Legal Notice</title>
<para>
This document can be distributed under the FDL
(<ulink url = "http://www.gnu.org">www.gnu.org</ulink>)
</para>
</legalnotice>
<pubdate>Tue Jun 5 20:07:49 2001</pubdate>
<abstract>
<para>
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.
</para>
</abstract>
</articleinfo>
<para>
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".
</para>
<section id="sec-nsstd" label="1"><title>Namespace std::</title>
<para>
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:
<itemizedlist>
<listitem><para>wrap your code in <command>namespace std {
... }</command> =&gt; This is not an option because only symbols
from the standard c++-library are defined in namespace std::.
</para></listitem>
<listitem><para>put a kind of
<emphasis>using-declaration</emphasis> in your source (either
<command>using namespace std;</command> or i.e. <command>using
std::string;</command>) =&gt; works well for source-files, but
cannot be used in header-files.
</para></listitem>
<listitem><para>use a <emphasis>fully qualified name</emphasis> for
each libstdc++-symbol (i.e. <command>std::string</command>,
<command>std::cout</command>) =&gt; can always be used
</para></listitem>
</itemizedlist>
</para>
<para>
Because there are many compilers which still use an implementation
that does not have the standard C++-library in namespace
<command>std::</command>, some care is required to support these as
well.
</para>
<para>
Namespace back-portability-issues are generally not a problem with
g++, because versions of g++ that do not have libstdc++ in
<command>std::</command> use <command>-fno-honor-std</command>
(ignore <command>std::</command>, <command>:: = std::</command>) by
default. That is, the responsibility for enabling or disabling
<command>std::</command> is on the user; the maintainer does not have
to care about it. This probably applies to some other compilers as
well.
</para>
<para>
The following sections list some possible solutions to support compilers
that cannot ignore std::.
</para>
<section id = "sec-gtkmm-hack" label = "1.1">
<title id="sec-gtkmm-hack.title">Using <emphasis>namespace
composition</emphasis> if the project uses a separate
namespace</title>
<para>
<ulink url = "http://gtkmm.sourceforge.net">Gtk--</ulink> defines
most of its classes in namespace Gtk::. Thus, it was possible to
adapt Gtk-- to namespace std:: by using a C++-feature called
<emphasis>namespace composition</emphasis>. This is what happens if
you put a <emphasis>using</emphasis>-declaration into a
namespace-definition: the imported symbol(s) gets imported into the
currently active namespace(s). For example:
<programlisting>
namespace Gtk {
using std::string;
class Window { ... }
}
</programlisting>
In this example, <command>std::string</command> gets imported into
namespace Gtk::. The result is that you don't have to use
<command>std::string</command> in this header, but still
<command>std::string</command> does not get imported into
the global namespace (::) unless the user does
<command>using namespace Gtk;</command> (which is not recommended
practice for Gtk--, so it is not a problem). Additionally, the
<command>using</command>-declarations are wrapped in macros that
are set based on autoconf-tests to either "" or i.e. <command>using
std::string;</command> (depending on whether the system has
libstdc++ in <command>std::</command> or not). (ideas from
<email>llewelly@dbritsch.dsl.xmission.com</email>, Karl Nelson
<email>kenelson@ece.ucdavis.edu</email>)
</para>
</section>
<section id = "sec-emptyns" label = "1.2">
<title id="sec-emptyns.title">Defining an empty namespace std</title>
<para>
By defining an (empty) namespace <command>std::</command> before
using it, you avoid getting errors on systems where no part of the
library is in namespace std:
<programlisting>
namespace std { }
using namespace std;
</programlisting>
</para>
</section>
<section id = "sec-avoidfqn" label = "1.3">
<title id="sec-avoidfqn.title">Avoid to use fully qualified names
(i.e. std::string)</title>
<para>
If some compilers complain about <command>using
std::string;</command>, and if the "hack" for gtk-- mentioned above
does not work, then it might be a good idea to define a macro
<symbol>NS_STD</symbol>, which is defined to either "" or "std"
based on an autoconf-test. Then you should be able to use
<command>NS_STD::string</command>, which will evaluate to
<command>::string</command> ("string in the global namespace") on
systems that do not put string in std::. (This is untested)
</para>
</section>
<section id = "sec-osprojects" label = "1.4">
<title id="sec-osprojects.title">How some open-source-projects deal
with this</title>
<para>
This information was gathered around May 2000. It may not be correct
by the time you read this.
</para>
<table><title>Namespace std:: in Open-Source programs</title>
<tgroup cols = "2">
<tbody>
<row>
<entry><ulink url = "http://www.clanlib.org">clanlib</ulink>
</entry>
<entry>usual</entry>
</row>
<row>
<entry><ulink url = "http://pingus.seul.org">pingus</ulink>
</entry>
<entry>usual</entry>
</row>
<row>
<entry><ulink url = "http://www.mozilla.org">mozilla</ulink>
</entry>
<entry>usual</entry>
</row>
<row>
<entry><ulink url = "http://www.mnemonic.org">mnemonic</ulink>
</entry> <entry>none</entry>
</row>
<row>
<entry><ulink url = "http://libsigc.sourceforge.net">
libsigc++</ulink></entry>
<entry>conservative-impl</entry>
</row>
</tbody>
</tgroup>
</table>
<table><title>Notations for categories</title>
<tgroup cols = "2">
<tbody>
<row>
<entry>usual</entry>
<entry>mostly fully qualified names and some
using-declarations (but not in headers)</entry>
</row>
<row>
<entry>none</entry> <entry>no namespace std at all</entry>
</row>
<row>
<entry>conservative-impl</entry>
<entry>wrap all
namespace-handling in macros to support compilers without
namespace-support (no libstdc++ used in headers)</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
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 <link linkend="sec-gtkmm-hack"
endterm="sec-gtkmm-hack.title">section on the gtkmm-hack</link>).
</para>
</section>
</section> <!-- end of namespace-section -->
<section id = "sec-nocreate" label = "2">
<title id="sec-nocreate.title">there is no ios::nocreate/ios::noreplace
in ISO 14882</title>
<para>
I have seen <command>ios::nocreate</command> 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.
</para>
<para>
For output streams, "nocreate" is probably the default, unless you
specify <command>std::ios::trunc</command> ? 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 <command>app</command>,
<command>ate</command> and <command>trunc</command> (except for
<command>app</command> ?).
</para>
</section>
<section id = "sec-stream::attach" label = "3">
<title id="sec-stream::attach.title"><command>stream::attach(int
fd)</command> is not in the standard any more</title>
<para>
Phil Edwards <email>pedwards@disaster.jaj.com</email> 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.
</para>
<para>
When using libstdc++-v3, you can use
<funcsynopsis>
<funcsynopsisinfo format="linespecific">
#include &lt;fstream&gt;
</funcsynopsisinfo>
<funcprototype>
<funcdef>
<function>basic_filebuf&lt;...&gt;::basic_filebuf&lt;...&gt;
</function>
</funcdef>
<paramdef>__c_file_type* <parameter>file</parameter></paramdef>
<paramdef>ios_base::open_mode <parameter>mode</parameter></paramdef>
<paramdef>int <parameter>size</parameter></paramdef>
</funcprototype>
</funcsynopsis>
but the the signature of this constructor has changed often, and
it might change again. For the current state of this, check
<ulink url="../ext/howto.html">the howto for extensions</ulink>.
</para>
<para>
For a portable solution (among systems which use
filedescriptors), you need to implement a subclass of
<command>std::streambuf</command> (or
<command>std::basic_streambuf&lt;..&gt;</command>) which opens a file
given a descriptor, and then pass an instance of this to the
stream-constructor (from the Josuttis-book).
</para>
</section>
<section id = "sec-headers" label = "4">
<title id="sec-headers.title">The new headers</title>
<para>
All new headers can be seen in this <ulink url="headers_cc.txt">
source-code</ulink>.
</para>
<para>
The old C++-headers (iostream.h etc.) are available, but gcc generates
a warning that you are using deprecated headers.
</para>
<section id = "sec-cheaders" label = "4.1">
<title id="sec-cheaders.title">New headers replacing C-headers</title>
<para>
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
<filename class="headerfile">&lt;math.h&gt;</filename>, you
should use <filename class =
"headerfile">&lt;cmath&gt;</filename>. In some cases this has
the advantage that the C++-header is more standardized than
the C-header (i.e. <filename
class="headerfile">&lt;ctime&gt;</filename> (almost)
corresponds to either <filename class =
"headerfile">&lt;time.h&gt;</filename> or <filename class =
"headerfile">&lt;sys/time.h&gt;</filename>).
The standard specifies that if you include the C-style header
(<filename class = "headerfile">&lt;math.h&gt;</filename> in
this case), the symbols will be available both in the global
namespace and in namespace <command>std::</command> (but
libstdc++ does not yet have fully compliant headers) On the
other hand, if you include only the new header (i.e. <filename
class = "headerfile">&lt;cmath&gt;</filename>), the symbols
will only be defined in namespace <command>std::</command>
(and macros will be converted to inline-functions).
</para>
<para>
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 <ulink
url="http://www.cantrip.org/cheaders.html">
www.cantrip.org</ulink>.
</para>
</section>
<section id = "sec-fstream-header" label = "4.2">
<title id="sec-fstream-header.title">
<filename class="headerfile">&lt;fstream&gt;</filename> does
not define <command>std::cout</command>,
<command>std::cin</command> etc.</title>
<para>
In earlier versions of the standard,
<filename class="headerfile">&lt;fstream.h&gt;</filename>,
<filename class="headerfile">&lt;ostream.h&gt;</filename>
and <filename class="headerfile">&lt;istream.h&gt;</filename>
used to define
<command>cout</command>, <command>cin</command> and so on. Because
of the templatized iostreams in libstdc++-v3, you need to include
<filename class = "headerfile">&lt;iostream&gt;</filename>
explicitly to define these.
</para>
</section>
</section>
<section id = "sec-iterators" label = "5">
<title id="sec-iterators.title">Iterators</title>
<para>
The following are not proper uses of iterators, but may be working
fixes for existing uses of iterators.
<itemizedlist>
<listitem><para>you cannot do
<command>ostream::operator&lt;&lt;(iterator)</command> to
print the address of the iterator =&gt; use
<command>operator&lt;&lt; &amp;*iterator</command> instead ?
</para>
</listitem>
<listitem><para>you cannot clear an iterator's reference
(<command>iterator = 0</command>) =&gt; use
<command>iterator = iterator_type();</command> ?
</para>
</listitem>
<listitem><para><command>if (iterator)</command> won't work any
more => use <command>if (iterator != iterator_type())</command>
?</para>
</listitem>
</itemizedlist>
</para>
</section>
<section id = "sec-macros" label = "6">
<title id="sec-macros.title">
Libc-macros (i.e. <command>isspace</command> from
<filename class = "headerfile">&lt;cctype&gt;</filename>)</title>
<para>
Glibc 2.0.x and 2.1.x define the
<filename class="headerfile">&lt;ctype.h&gt;</filename>
-functionality as macros (isspace, isalpha etc.). Libstdc++-v3
"shadows" these macros as described in the <link
linkend="sec-cheaders" endterm="sec-cheaders.title">section about
c-headers</link>.
</para>
<para>
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:
<programlisting>
#include &lt;cctype&gt;
int main() { std::isspace('X'); }
</programlisting>
will result in something like this (unless using g++-v3):
<programlisting>
std:: (__ctype_b[(int) ( ( 'X' ) )] &amp; (unsigned short int)
_ISspace ) ;
</programlisting>
</para>
<para>
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 ?).
</para>
<para>
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 <filename
class="headerfile">&lt;ctype.h&gt;</filename> to define functions
instead of macros:
<programlisting>
// This keeps isalnum, et al from being propagated as macros.
#if __linux__
#define __NO_CTYPE 1
#endif
[ now include &lt;ctype.h&gt; ]
</programlisting>
</para>
<para>
Another problem arises if you put a <command>using namespace
std;</command> declaration at the top, and include <filename class
= "headerfile">&lt;ctype.h&gt;</filename>. This will result in
ambiguities between the definitions in the global namespace
(<filename class = "headerfile">&lt;ctype.h&gt;</filename>) and the
definitions in namespace <command>std::</command>
(<command>&lt;cctype&gt;</command>).
</para>
<para>
The solution to this problem was posted to the libstdc++-v3
mailing-list:
Benjamin Kosnik <email>bkoz@redhat.com</email> writes:
<quote>
--enable-cshadow-headers is currently broken. As a result, shadow
headers are not being searched....
</quote>
This is now outdated, but gcc 3.0 still does not have fully
compliant "shadow headers".
</para>
</section>
<section id="sec-stream-state" label="7">
<title id="sec-stream-state.title">State of streams</title>
<para>
At least some older implementations don't have
<command>std::ios_base</command>, so you should use
<command>std::ios::badbit</command>, <command>std::ios::failbit</command>
and <command>std::ios::eofbit</command> and
<command>std::ios::goodbit</command>.
</para>
</section>
<section id="sec-vector-at" label="8">
<title>vector::at is missing (i.e. gcc 2.95.x)</title>
<para>
One solution is to add an autoconf-test for this:
<programlisting>
AC_MSG_CHECKING(for container::at)
AC_TRY_COMPILE(
[
#include &lt;vector&gt;
#include &lt;deque&gt;
#include &lt;string&gt;
using namespace std;
],
[
deque&lt;int&gt; test_deque(3);
test_deque.at(2);
vector&lt;int&gt; 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)])
</programlisting>
If you are using other (non-GNU) compilers it might be a good idea
to check for <command>string::at</command> separately.
</para>
</section>
<section id="sec-eof" label="9">
<title>Using std::char_traits&lt;char&gt;::eof()</title>
<para>
<programlisting>
#ifdef HAVE_CHAR_TRAITS
#define CPP_EOF std::char_traits&lt;char&gt;::eof()
#else
#define CPP_EOF EOF
#endif
</programlisting>
</para>
</section>
<section id="sec-string-clear" label="10">
<title>Using string::clear()/string::erase()</title>
<para>
There are two functions for deleting the contents of a string:
<command>clear</command> and <command>erase</command> (the latter
returns the string).
<programlisting>
void
clear() { _M_mutate(0, this->size(), 0); }
</programlisting>
<programlisting>
basic_string&amp;
erase(size_type __pos = 0, size_type __n = npos)
{
return this->replace(_M_check(__pos), _M_fold(__pos, __n),
_M_data(), _M_data());
}
</programlisting>
The implementation of <command>erase</command> seems to be more
complicated (from libstdc++-v3), but <command>clear</command> is not
implemented in gcc 2.95.x's libstdc++, so you should use
<command>erase</command> (which is probably faster than
<command>operator=(charT*)</command>).
</para>
</section>
<section id="sec-scan-form" label="11">
<title>GNU Extensions ostream::form and istream::scan</title>
<para>
These are not supported any more - use
<link linkend="sec-stringstream" endterm="sec-stringstream.title">
stringstreams</link> instead.
</para>
</section>
<section id="sec-stringstream" label="12">
<title>Using stringstreams</title>
<para>
Libstdc++-v3 provides the new
<command>i/ostringstream</command>-classes, (<filename
class="headerfile">&lt;sstream&gt;</filename>), but for compatibility
with older implementations you still have to use
<command>i/ostrstream</command> (<filename
class="headerfile">&lt;strstream&gt;</filename>):
<programlisting>
#ifdef HAVE_SSTREAM
#include &lt;sstream&gt;
#else
#include &lt;strstream&gt;
#endif
</programlisting>
<itemizedlist>
<listitem><para> <command>strstream</command> is considered to be
deprecated
</para>
</listitem>
<listitem><para> <command>strstream</command> is limited to
<command>char</command>
</para>
</listitem>
<listitem><para> with <command>ostringstream</command> you don't
have to take care of terminating the string or freeing its
memory
</para>
</listitem>
<listitem><para> <command>istringstream</command> can be re-filled
(clear(); str(input);)
</para>
</listitem>
</itemizedlist>
</para>
<para>
You can then use output-stringstreams like this:
<programlisting>
#ifdef HAVE_SSTREAM
std::ostringstream oss;
#else
std::ostrstream oss;
#endif
oss &lt;&lt; "Name=" &lt;&lt; m_name &lt;&lt; ", number=" &lt;&lt; m_number &lt;&lt; std::endl;
...
#ifndef HAVE_SSTREAM
oss &lt;&lt; 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
</programlisting>
</para>
<para>
Input-stringstreams can be used similarly:
<programlisting>
std::string input;
...
#ifdef HAVE_SSTREAM
std::istringstream iss(input);
#else
std::istrstream iss(input.c_str());
#endif
int i;
iss >> i;
</programlisting>
One (the only?) restriction is that an istrstream cannot be re-filled:
<programlisting>
std::istringstream iss(numerator);
iss >> m_num;
// this is not possible with istrstream
iss.clear();
iss.str(denominator);
iss >> m_den;
</programlisting>
If you don't care about speed, you can put these conversions in
a template-function:
<programlisting>
template &lt;class X&gt;
void fromString(const string&amp; input, X&amp; 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;
}
</programlisting>
Another example of using stringstreams is in <ulink
url="../21_strings/howto.html">this howto</ulink>.
</para>
<para>
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.
</para>
</section>
<section id = "sec-about" label="13">
<title id="sec-about.title">About...</title>
<para>
Please send any experience, additions, corrections or questions to
<ulink url = "mailto:fnatter@gmx.net">fnatter@gmx.net</ulink> or for
discussion to the libstdc++-v3-mailing-list.
</para>
</section>
</article>
<!-- this is now obsolete, since the nwalsh-stylesheet generates an index
<para>
<itemizedlist>
<listitem><para>
<link linkend = "sec-nsstd" endterm = "sec-nsstd.title"/>
</para></listitem>
<listitem><para>
<link linkend = "sec-nocreate" endterm = "sec-nocreate.title"/>
</para></listitem>
<listitem><para>
<link linkend = "sec-stream::attach"
endterm = "sec-stream::attach.title"/>
</para></listitem>
<listitem><para>
<link linkend = "sec-headers" endterm = "sec-headers.title"/>
</para></listitem>
<listitem><para>
<link linkend = "sec-iterators" endterm = "sec-iterators.title"/>
</para></listitem>
<listitem><para>
<link linkend = "sec-macros" endterm = "sec-macros.title"/>
</para></listitem>
<listitem><para>
<link linkend = "sec-about" endterm = "sec-about.title"/>
</para></listitem>
</itemizedlist>
</para>
-->
<!--
Local Variables:
compile-command: "xsltproc -o porting-howto.html docbook-xslt/docbook/html/docbook.xsl porting-howto.xml"
End:
-->
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment