2 Copyright 2001, 2003, 2004, 2012 Daryle Walker.
4 Distributed under the Boost Software License, Version 1.0.
6 See accompanying file LICENSE_1_0.txt
7 or copy at http://boost.org/LICENSE_1_0.txt
10 [article Base_From_Member
12 [authors [Walker, Daryle]]
13 [copyright 2001, 2003, 2004, 2012 Daryle Walker]
15 Distributed under the Boost Software License, Version 1.0.
16 (See accompanying file LICENSE_1_0.txt or copy at
17 [@http://www.boost.org/LICENSE_1_0.txt])
23 When developing a class, sometimes a base class needs to be initialized
24 with a member of the current class. As a na\u00EFve example:
26 #include <streambuf> /* for std::streambuf */
27 #include <ostream> /* for std::ostream */
30 : public std::streambuf
33 explicit fdoutbuf( int fd );
43 explicit fdostream( int fd )
44 : buf( fd ), std::ostream( &buf ) {}
48 This is undefined because C++'s initialization order mandates that the base
49 class is initialized before the member it uses. [@http://www.moocat.org R.
50 Samuel Klatchko] developed a way around this by using the initialization
51 order in his favor. Base classes are intialized in order of declaration, so
52 moving the desired member to another base class, that is initialized before
53 the desired base class, can ensure proper initialization.
55 A custom base class can be made for this idiom:
57 #include <streambuf> /* for std::streambuf */
58 #include <ostream> /* for std::ostream */
61 : public std::streambuf
64 explicit fdoutbuf( int fd );
68 struct fdostream_pbase
72 explicit fdostream_pbase( int fd )
77 : private fdostream_pbase
80 typedef fdostream_pbase pbase_type;
81 typedef std::ostream base_type;
84 explicit fdostream( int fd )
85 : pbase_type( fd ), base_type( &sbuffer ) {}
89 Other projects can use similar custom base classes. The technique is basic
90 enough to make a template, with a sample template class in this library.
91 The main template parameter is the type of the enclosed member. The
92 template class has several (explicit) constructor member templates, which
93 implicitly type the constructor arguments and pass them to the member. The
94 template class uses implicit copy construction and assignment, cancelling
95 them if the enclosed member is non-copyable.
97 Manually coding a base class may be better if the construction and/or
98 copying needs are too complex for the supplied template class, or if the
99 compiler is not advanced enough to use it.
101 Since base classes are unnamed, a class cannot have multiple (direct) base
102 classes of the same type. The supplied template class has an extra template
103 parameter, an integer, that exists solely to provide type differentiation.
104 This parameter has a default value so a single use of a particular member
105 type does not need to concern itself with the integer.
111 #include <type_traits> /* exposition only */
113 #ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY
114 #define BOOST_BASE_FROM_MEMBER_MAX_ARITY 10
117 template < typename MemberType, int UniqueID = 0 >
118 class boost::base_from_member
123 #if ``['C++11 is in use]``
124 template< typename ...T >
125 explicit constexpr base_from_member( T&& ...x )
126 noexcept( std::is_nothrow_constructible<MemberType, T...>::value );
130 template< typename T1 >
131 explicit base_from_member( T1 x1 );
133 template< typename T1, typename T2 >
134 base_from_member( T1 x1, T2 x2 );
138 template< typename T1, typename T2, typename T3, typename T4,
139 typename T5, typename T6, typename T7, typename T8, typename T9,
141 base_from_member( T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7,
142 T8 x8, T9 x9, T10 x10 );
146 template < typename MemberType, int UniqueID >
147 class base_from_member<MemberType&, UniqueID>
152 explicit constexpr base_from_member( MemberType& x )
156 The class template has a first template parameter `MemberType` representing
157 the type of the based-member. It has a last template parameter `UniqueID`,
158 that is an `int`, to differentiate between multiple base classes that use
159 the same based-member type. The last template parameter has a default value
160 of zero if it is omitted. The class template has a protected data member
161 called `member` that the derived class can use for later base classes (or
164 If the appropriate features of C++11 are present, there will be a single
165 constructor template. It implements ['perfect forwarding] to the best
166 constructor call of `member` (if any). The constructor template is marked
167 both `constexpr` and `explicit`. The former will be ignored if the
168 corresponding inner constructor call (of `member`) does not have the marker.
169 The latter binds the other way; always taking effect, even when the inner
170 constructor call does not have the marker. The constructor template
171 propagates the `noexcept` status of the inner constructor call. (The
172 constructor template has a trailing parameter with a default value that
173 disables the template when its signature is too close to the signatures of
174 the automatically-defined non-template copy- and/or move-constructors of
177 On earlier-standard compilers, there is a default constructor and several
178 constructor member templates. These constructor templates can take as many
179 arguments (currently up to ten) as possible and pass them to a constructor
182 A specialization for member references offers a single constructor taking
183 a `MemberType&`, which is the only way to initialize a reference.
185 Since C++ does not allow any way to explicitly state the template parameters
186 of a templated constructor, make sure that the arguments are already close
187 as possible to the actual type used in the data member's desired constructor.
188 Explicit conversions may be necessary.
190 The `BOOST_BASE_FROM_MEMBER_MAX_ARITY` macro constant specifies the maximum
191 argument length for the constructor templates. The constant may be overridden
192 if more (or less) argument configurations are needed. The constant may be
193 read for code that is expandable like the class template and needs to
194 maintain the same maximum size. (Example code would be a class that uses
195 this class template as a base class for a member with a flexible set of
196 constructors.) This constant is ignored when C++11 features are present.
202 With the starting example, the `fdoutbuf` sub-object needs to be
203 encapsulated in a base class that is inheirited before `std::ostream`.
205 #include <boost/utility/base_from_member.hpp>
207 #include <streambuf> // for std::streambuf
208 #include <ostream> // for std::ostream
211 : public std::streambuf
214 explicit fdoutbuf( int fd );
219 : private boost::base_from_member<fdoutbuf>
220 , public std::ostream
223 typedef boost::base_from_member<fdoutbuf> pbase_type;
224 typedef std::ostream base_type;
227 explicit fdostream( int fd )
228 : pbase_type( fd ), base_type( &member ){}
232 The base-from-member idiom is an implementation detail, so it should not
233 be visible to the clients (or any derived classes) of `fdostream`. Due to
234 the initialization order, the `fdoutbuf` sub-object will get initialized
235 before the `std::ostream` sub-object does, making the former sub-object
236 safe to use in the latter sub-object's construction. Since the `fdoutbuf`
237 sub-object of the final type is the only sub-object with the name `member`
238 that name can be used unqualified within the final class.
244 The base-from-member class templates should commonly involve only one
245 base-from-member sub-object, usually for attaching a stream-buffer to an
246 I/O stream. The next example demonstrates how to use multiple
247 base-from-member sub-objects and the resulting qualification issues.
249 #include <boost/utility/base_from_member.hpp>
251 #include <cstddef> /* for NULL */
264 switcher( double, int * );
271 flow_regulator( switcher &, switcher & );
275 template < unsigned Size >
279 explicit fan( switcher );
284 : private boost::base_from_member<an_int>
285 , private boost::base_from_member<switcher>
286 , private boost::base_from_member<switcher, 1>
287 , private boost::base_from_member<switcher, 2>
288 , protected flow_regulator
292 typedef boost::base_from_member<an_int> pbase0_type;
293 typedef boost::base_from_member<switcher> pbase1_type;
294 typedef boost::base_from_member<switcher, 1> pbase2_type;
295 typedef boost::base_from_member<switcher, 2> pbase3_type;
297 typedef flow_regulator base1_type;
298 typedef fan<6> base2_type;
305 system::system( double x )
308 , pbase2_type( -16, &this->pbase0_type::member.y )
309 , pbase3_type( x, static_cast<int *>(NULL) )
310 , base1_type( pbase3_type::member, pbase1_type::member )
311 , base2_type( pbase2_type::member )
316 The final class has multiple sub-objects with the name `member`, so any
317 use of that name needs qualification by a name of the appropriate base
318 type. (Using `typedef`s ease mentioning the base types.) However, the fix
319 introduces a new problem when a pointer is needed. Using the address
320 operator with a sub-object qualified with its class's name results in a
321 pointer-to-member (here, having a type of `an_int boost::base_from_member<
322 an_int, 0> :: *`) instead of a pointer to the member (having a type of
323 `an_int *`). The new problem is fixed by qualifying the sub-object with
324 `this->` and is needed just for pointers, and not for references or values.
326 There are some argument conversions in the initialization. The constructor
327 argument for `pbase0_type` is converted from `double` to `float`. The first
328 constructor argument for `pbase2_type` is converted from `int` to `double`.
329 The second constructor argument for `pbase3_type` is a special case of
330 necessary conversion; all forms of the null-pointer literal in C++ (except
331 `nullptr` from C++11) also look like compile-time integral expressions, so
332 C++ always interprets such code as an integer when it has overloads that can
333 take either an integer or a pointer. The last conversion is necessary for the
334 compiler to call a constructor form with the exact pointer type used in
335 `switcher`'s constructor. (If C++11's `nullptr` is used, it still needs a
336 conversion if multiple pointer types can be accepted in a constructor call
337 but `std::nullptr_t` cannot.)
341 [section Acknowledgments]
343 * [@http://www.boost.org/people/ed_brey.htm Ed Brey] suggested some interface
346 * [@http://www.moocat.org R. Samuel Klatchko] ([@mailto:rsk@moocat.org
347 rsk@moocat.org], [@mailto:rsk@brightmail.com rsk@brightmail.com]) invented
348 the idiom of how to use a class member for initializing a base class.
350 * [@http://www.boost.org/people/dietmar_kuehl.htm Dietmar Kuehl] popularized the
351 base-from-member idiom in his [@http://www.informatik.uni-konstanz.de/~kuehl/c++/iostream/
352 IOStream example classes].
354 * Jonathan Turkanis supplied an implementation of generating the constructor
355 templates that can be controlled and automated with macros. The
356 implementation uses the [@../../../preprocessor/index.html Preprocessor library].
358 * [@http://www.boost.org/people/daryle_walker.html">Daryle Walker] started the
359 library. Contributed the test file [@../../base_from_member_test.cpp
360 base_from_member_test.cpp].