Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/utility/base_from_member.html @ 12

Last change on this file since 12 was 12, checked in by landauf, 17 years ago

added boost

File size: 12.2 KB
Line 
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
2<html>
3<head>
4<title>Boost: Base-from-Member Idiom Documentation</title>
5</head>
6
7<body bgcolor="white" link="blue" text="black" vlink="purple" alink="red"> 
8<h1><img src="../../boost.png" alt="C++ Boost" align="middle"
9width="277" height="86">Base-from-Member Idiom</h1>
10
11<p>The class template <code>boost::base_from_member</code> provides
12a workaround for a class that needs to initialize a base class with a
13member.  The class template is in <cite><a
14href="../../boost/utility/base_from_member.hpp">boost/utility/base_from_member.hpp</a></cite>
15which is included in <i><a href="../../boost/utility.hpp">boost/utility.hpp</a></i>.</p>
16
17<p>There is test/example code in <cite><a
18href="base_from_member_test.cpp">base_from_member_test.cpp</a></cite>.</p>
19
20<h2><a name="contents">Contents</a></h2>
21
22<ul>
23        <li><a href="#contents">Contents</a></li>
24        <li><a href="#rationale">Rationale</a></li>
25        <li><a href="#synopsis">Synopsis</a></li>
26        <li><a href="#usage">Usage</a></li>
27        <li><a href="#example">Example</a></li>
28        <li><a href="#credits">Credits</a>
29                <ul>
30                        <li><a href="#contributors">Contributors</a></li>
31                </ul></li>
32</ul>
33
34<h2><a name="rationale">Rationale</a></h2>
35
36<p>When developing a class, sometimes a base class needs to be
37initialized with a member of the current class.  As a na&iuml;ve
38example:</p>
39
40<blockquote><pre>
41#include &lt;streambuf&gt;  <i>// for std::streambuf</i>
42#include &lt;ostream&gt;    <i>// for std::ostream</i>
43
44class fdoutbuf
45    : public std::streambuf
46{
47public:
48    explicit fdoutbuf( int fd );
49    //...
50};
51
52class fdostream
53    : public std::ostream
54{
55protected:
56    fdoutbuf buf;
57public:
58    explicit fdostream( int fd )
59        : buf( fd ), std::ostream( &amp;buf )
60        {}
61    //...
62};
63</pre></blockquote>
64
65<p>This is undefined because C++'s initialization order mandates that
66the base class is initialized before the member it uses.  <a
67href="http://www.moocat.org">R. Samuel Klatchko</a> developed a way
68around this by using the initialization order in his favor.  Base
69classes are intialized in order of declaration, so moving the desired
70member to another base class, that is initialized before the desired
71base class, can ensure proper initialization.</p>
72
73<p>A custom base class can be made for this idiom:</p>
74
75<blockquote><pre>
76#include &lt;streambuf&gt;  <i>// for std::streambuf</i>
77#include &lt;ostream&gt;    <i>// for std::ostream</i>
78
79class fdoutbuf
80    : public std::streambuf
81{
82public:
83    explicit fdoutbuf( int fd );
84    //...
85};
86
87struct fdostream_pbase
88{
89    fdoutbuf sbuffer;
90
91    explicit fdostream_pbase( int fd )
92        : sbuffer( fd )
93        {}
94};
95
96class fdostream
97    : private fdostream_pbase
98    , public std::ostream
99{
100    typedef fdostream_pbase  pbase_type;
101    typedef std::ostream     base_type;
102
103public:
104    explicit fdostream( int fd )
105        : pbase_type( fd ), base_type( &amp;sbuffer )
106        {}
107    //...
108};
109</pre></blockquote>
110
111<p>Other projects can use similar custom base classes.  The technique
112is basic enough to make a template, with a sample template class in
113this library.  The main template parameter is the type of the enclosed
114member.  The template class has several (explicit) constructor member
115templates, which implicitly type the constructor arguments and pass them
116to the member.  The template class uses implicit copy construction and
117assignment, cancelling them if the enclosed member is non-copyable.</p>
118
119<p>Manually coding a base class may be better if the construction
120and/or copying needs are too complex for the supplied template class,
121or if the compiler is not advanced enough to use it.</p>
122
123<p>Since base classes are unnamed, a class cannot have multiple (direct)
124base classes of the same type.  The supplied template class has an
125extra template parameter, an integer, that exists solely to provide type
126differentiation.  This parameter has a default value so a single use of a
127particular member type does not need to concern itself with the integer.</p>
128
129<h2><a name="synopsis">Synopsis</a></h2>
130
131<blockquote><pre>
132#ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY
133#define BOOST_BASE_FROM_MEMBER_MAX_ARITY  10
134#endif
135
136template &lt; typename MemberType, int UniqueID = 0 &gt;
137class boost::base_from_member
138{
139protected:
140    MemberType  member;
141
142    base_from_member();
143
144    template&lt; typename T1 &gt;
145    explicit  base_from_member( T1 x1 );
146
147    template&lt; typename T1, typename T2 &gt;
148    base_from_member( T1 x1, T2 x2 );
149
150    //...
151
152    template&lt; typename T1, typename T2, typename T3, typename T4,
153     typename T5, typename T6, typename T7, typename T8, typename T9,
154     typename T10 &gt;
155    base_from_member( T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7,
156     T8 x8, T9 x9, T10 x10 );
157};
158</pre></blockquote>
159
160<p>The class template has a first template parameter
161<var>MemberType</var> representing the type of the based-member.
162It has a last template parameter <var>UniqueID</var>, that is an
163<code>int</code>, to differentiate between multiple base classes that use
164the same based-member type.  The last template parameter has a default
165value of zero if it is omitted.  The class template has a protected
166data member called <var>member</var> that the derived class can use
167for later base classes (or itself).</p>
168
169<p>There is a default constructor and several constructor member
170templates.  These constructor templates can take as many arguments
171(currently up to ten) as possible and pass them to a constructor of
172the data member.  Since C++ does not allow any way to explicitly state
173the template parameters of a templated constructor, make sure that
174the arguments are already close as possible to the actual type used in
175the data member's desired constructor.</p>
176
177<p>The <var>BOOST_BASE_FROM_MEMBER_MAX_ARITY</var> macro constant specifies
178the maximum argument length for the constructor templates.  The constant
179may be overridden if more (or less) argument configurations are needed.  The
180constant may be read for code that is expandable like the class template and
181needs to maintain the same maximum size.  (Example code would be a class that
182uses this class template as a base class for a member with a flexible set of
183constructors.)</p>
184
185<h2><a name="usage">Usage</a></h2>
186
187<p>With the starting example, the <code>fdoutbuf</code> sub-object needs
188to be encapsulated in a base class that is inheirited before
189<code>std::ostream</code>.</p>
190
191<blockquote><pre>
192#include &lt;boost/utility/base_from_member.hpp&gt;
193
194#include &lt;streambuf&gt;  <i>// for std::streambuf</i>
195#include &lt;ostream&gt;    <i>// for std::ostream</i>
196
197class fdoutbuf
198    : public std::streambuf
199{
200public:
201    explicit fdoutbuf( int fd );
202    //...
203};
204
205class fdostream
206    : private boost::base_from_member&lt;fdoutbuf&gt;
207    , public std::ostream
208{
209    // Helper typedef's
210    typedef boost::base_from_member&lt;fdoutbuf&gt;  pbase_type;
211    typedef std::ostream                        base_type;
212
213public:
214    explicit fdostream( int fd )
215        : pbase_type( fd ), base_type( &amp;member )
216        {}
217    //...
218};
219</pre></blockquote>
220
221<p>The base-from-member idiom is an implementation detail, so it
222should not be visible to the clients (or any derived classes) of
223<code>fdostream</code>.  Due to the initialization order, the
224<code>fdoutbuf</code> sub-object will get initialized before the
225<code>std::ostream</code> sub-object does, making the former
226sub-object safe to use in the latter sub-object's construction.  Since the
227<code>fdoutbuf</code> sub-object of the final type is the only sub-object
228with the name &quot;member,&quot; that name can be used
229unqualified within the final class.</p>
230
231<h2><a name="example">Example</a></h2>
232
233<p>The base-from-member class templates should commonly involve
234only one base-from-member sub-object, usually for attaching a
235stream-buffer to an I/O stream.  The next example demonstrates how
236to use multiple base-from-member sub-objects and the resulting
237qualification issues.</p>
238
239<blockquote><pre>
240#include &lt;boost/utility/base_from_member.hpp&gt;
241
242#include &lt;cstddef&gt;  <i>// for NULL</i>
243
244struct an_int
245{
246    int  y;
247
248    an_int( float yf );
249};
250
251class switcher
252{
253public:
254    switcher();
255    switcher( double, int * );
256    //...
257};
258
259class flow_regulator
260{
261public:
262    flow_regulator( switcher &amp;, switcher &amp; );
263    //...
264};
265
266template &lt; unsigned Size &gt;
267class fan
268{
269public:
270    explicit fan( switcher );
271    //...
272};
273
274class system
275    : private boost::base_from_member&lt;an_int&gt;
276    , private boost::base_from_member&lt;switcher&gt;
277    , private boost::base_from_member&lt;switcher, 1&gt;
278    , private boost::base_from_member&lt;switcher, 2&gt;
279    , protected flow_regulator
280    , public fan&lt;6&gt;
281{
282    // Helper typedef's
283    typedef boost::base_from_member&lt;an_int&gt;       pbase0_type;
284    typedef boost::base_from_member&lt;switcher&gt;     pbase1_type;
285    typedef boost::base_from_member&lt;switcher, 1&gt;  pbase2_type;
286    typedef boost::base_from_member&lt;switcher, 2&gt;  pbase3_type;
287
288    typedef flow_regulator  base1_type;
289    typedef fan&lt;6&gt;          base2_type;
290
291public:
292    system( double x );
293    //...
294};
295
296system::system( double x )
297    : pbase0_type( 0.2 )
298    , pbase1_type()
299    , pbase2_type( -16, &amp;this-&gt;pbase0_type::member )
300    , pbase3_type( x, static_cast&lt;int *&gt;(NULL) )
301    , base1_type( pbase3_type::member, pbase1_type::member )
302    , base2_type( pbase2_type::member )
303{
304    //...
305}
306</pre></blockquote>
307
308<p>The final class has multiple sub-objects with the name
309&quot;member,&quot; so any use of that name needs qualification by
310a name of the appropriate base type.  (Using <code>typedef</code>s
311ease mentioning the base types.)  However, the fix introduces a new
312problem when a pointer is needed.  Using the address operator with
313a sub-object qualified with its class's name results in a pointer-to-member
314(here, having a type of <code>an_int boost::base_from_member&lt;an_int,
3150&gt; :: *</code>) instead of a pointer to the member (having a type of
316<code>an_int *</code>).  The new problem is fixed by qualifying the
317sub-object with &quot;<code>this-&gt;</code>,&quot; and is needed just
318for pointers, and not for references or values.</p>
319
320<p>There are some argument conversions in the initialization.  The
321constructor argument for <code>pbase0_type</code> is converted from
322<code>double</code> to <code>float</code>.  The first constructor
323argument for <code>pbase2_type</code> is converted from <code>int</code>
324to <code>double</code>.  The second constructor argument for
325<code>pbase3_type</code> is a special case of necessary conversion; all
326forms of the null-pointer literal in C++ also look like compile-time
327integral expressions, so C++ always interprets such code as an integer
328when it has overloads that can take either an integer or a pointer.  The
329last conversion is necessary for the compiler to call a constructor form
330with the exact pointer type used in <code>switcher</code>'s constructor.</p>
331
332<h2><a name="credits">Credits</a></h2>
333
334<h3><a name="contributors">Contributors</a></h3>
335
336<dl>
337        <dt><a href="../../people/ed_brey.htm">Ed Brey</a>
338        <dd>Suggested some interface changes.
339
340        <dt><a href="http://www.moocat.org">R. Samuel Klatchko</a> (<a
341        href="mailto:rsk@moocat.org">rsk@moocat.org</a>, <a
342        href="mailto:rsk@brightmail.com">rsk@brightmail.com</a>)
343        <dd>Invented the idiom of how to use a class member for initializing
344                a base class.
345
346        <dt><a href="../../people/dietmar_kuehl.htm">Dietmar Kuehl</a>
347        <dd>Popularized the base-from-member idiom in his
348                <a href="http://www.informatik.uni-konstanz.de/~kuehl/c++/iostream/">IOStream
349                example classes</a>.
350
351        <dt>Jonathan Turkanis
352        <dd>Supplied an implementation of generating the constructor templates that
353                can be controlled and automated with macros.  The implementation uses
354                the <a href="../preprocessor/index.html">Preprocessor library</a>.
355
356        <dt><a href="../../people/daryle_walker.html">Daryle Walker</a>
357        <dd>Started the library.  Contributed the test file <cite><a
358                href="base_from_member_test.cpp">base_from_member_test.cpp</a></cite>.
359</dl>
360
361<hr>
362
363<p>Revised: 28 August 2004</p>
364
365<p>Copyright 2001, 2003, 2004 Daryle Walker.  Use, modification, and distribution
366are subject to the Boost Software License, Version 1.0.  (See accompanying
367file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or a copy at &lt;<a
368href="http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</a>&gt;.)</p>
369
370</body>
371</html>
Note: See TracBrowser for help on using the repository browser.