]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/utility/doc/base_from_member.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / utility / doc / base_from_member.qbk
CommitLineData
7c673cae
FG
1[/
2 Copyright 2001, 2003, 2004, 2012 Daryle Walker.
3
4 Distributed under the Boost Software License, Version 1.0.
5
6 See accompanying file LICENSE_1_0.txt
7 or copy at http://boost.org/LICENSE_1_0.txt
8]
9
10[article Base_From_Member
11 [quickbook 1.5]
12 [authors [Walker, Daryle]]
13 [copyright 2001, 2003, 2004, 2012 Daryle Walker]
14 [license
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])
18 ]
19]
20
21[section Rationale]
22
23When developing a class, sometimes a base class needs to be initialized
24with a member of the current class. As a na\u00EFve example:
25
26 #include <streambuf> /* for std::streambuf */
27 #include <ostream> /* for std::ostream */
28
29 class fdoutbuf
30 : public std::streambuf
31 {
32 public:
33 explicit fdoutbuf( int fd );
34 //...
35 };
36
37 class fdostream
38 : public std::ostream
39 {
40 protected:
41 fdoutbuf buf;
42 public:
43 explicit fdostream( int fd )
44 : buf( fd ), std::ostream( &buf ) {}
45 //...
46 };
47
48This is undefined because C++'s initialization order mandates that the base
49class is initialized before the member it uses. [@http://www.moocat.org R.
50Samuel Klatchko] developed a way around this by using the initialization
51order in his favor. Base classes are intialized in order of declaration, so
52moving the desired member to another base class, that is initialized before
53the desired base class, can ensure proper initialization.
54
55A custom base class can be made for this idiom:
56
57 #include <streambuf> /* for std::streambuf */
58 #include <ostream> /* for std::ostream */
59
60 class fdoutbuf
61 : public std::streambuf
62 {
63 public:
64 explicit fdoutbuf( int fd );
65 //...
66 };
67
68 struct fdostream_pbase
69 {
70 fdoutbuf sbuffer;
71
72 explicit fdostream_pbase( int fd )
73 : sbuffer( fd ) {}
74 };
75
76 class fdostream
77 : private fdostream_pbase
78 , public std::ostream
79 {
80 typedef fdostream_pbase pbase_type;
81 typedef std::ostream base_type;
82
83 public:
84 explicit fdostream( int fd )
85 : pbase_type( fd ), base_type( &sbuffer ) {}
86 //...
87 };
88
89Other projects can use similar custom base classes. The technique is basic
90enough to make a template, with a sample template class in this library.
91The main template parameter is the type of the enclosed member. The
92template class has several (explicit) constructor member templates, which
93implicitly type the constructor arguments and pass them to the member. The
94template class uses implicit copy construction and assignment, cancelling
95them if the enclosed member is non-copyable.
96
97Manually coding a base class may be better if the construction and/or
98copying needs are too complex for the supplied template class, or if the
99compiler is not advanced enough to use it.
100
101Since base classes are unnamed, a class cannot have multiple (direct) base
102classes of the same type. The supplied template class has an extra template
103parameter, an integer, that exists solely to provide type differentiation.
104This parameter has a default value so a single use of a particular member
105type does not need to concern itself with the integer.
106
107[endsect]
108
109[section Synopsis]
110
111 #include <type_traits> /* exposition only */
112
113 #ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY
114 #define BOOST_BASE_FROM_MEMBER_MAX_ARITY 10
115 #endif
116
117 template < typename MemberType, int UniqueID = 0 >
118 class boost::base_from_member
119 {
120 protected:
121 MemberType member;
122
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 );
127 #else
128 base_from_member();
129
130 template< typename T1 >
131 explicit base_from_member( T1 x1 );
132
133 template< typename T1, typename T2 >
134 base_from_member( T1 x1, T2 x2 );
135
136 //...
137
138 template< typename T1, typename T2, typename T3, typename T4,
139 typename T5, typename T6, typename T7, typename T8, typename T9,
140 typename T10 >
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 );
143 #endif
144 };
145
146 template < typename MemberType, int UniqueID >
147 class base_from_member<MemberType&, UniqueID>
148 {
149 protected:
150 MemberType& member;
151
152 explicit constexpr base_from_member( MemberType& x )
153 noexcept;
154 };
155
156The class template has a first template parameter `MemberType` representing
157the type of the based-member. It has a last template parameter `UniqueID`,
158that is an `int`, to differentiate between multiple base classes that use
159the same based-member type. The last template parameter has a default value
160of zero if it is omitted. The class template has a protected data member
161called `member` that the derived class can use for later base classes (or
162itself).
163
164If the appropriate features of C++11 are present, there will be a single
165constructor template. It implements ['perfect forwarding] to the best
166constructor call of `member` (if any). The constructor template is marked
167both `constexpr` and `explicit`. The former will be ignored if the
168corresponding inner constructor call (of `member`) does not have the marker.
169The latter binds the other way; always taking effect, even when the inner
170constructor call does not have the marker. The constructor template
171propagates the `noexcept` status of the inner constructor call. (The
172constructor template has a trailing parameter with a default value that
173disables the template when its signature is too close to the signatures of
174the automatically-defined non-template copy- and/or move-constructors of
175`base_from_member`.)
176
177On earlier-standard compilers, there is a default constructor and several
178constructor member templates. These constructor templates can take as many
179arguments (currently up to ten) as possible and pass them to a constructor
180of the data member.
181
182A specialization for member references offers a single constructor taking
183a `MemberType&`, which is the only way to initialize a reference.
184
185Since C++ does not allow any way to explicitly state the template parameters
186of a templated constructor, make sure that the arguments are already close
187as possible to the actual type used in the data member's desired constructor.
188Explicit conversions may be necessary.
189
190The `BOOST_BASE_FROM_MEMBER_MAX_ARITY` macro constant specifies the maximum
191argument length for the constructor templates. The constant may be overridden
192if more (or less) argument configurations are needed. The constant may be
193read for code that is expandable like the class template and needs to
194maintain the same maximum size. (Example code would be a class that uses
195this class template as a base class for a member with a flexible set of
196constructors.) This constant is ignored when C++11 features are present.
197
198[endsect]
199
200[section Usage]
201
202With the starting example, the `fdoutbuf` sub-object needs to be
203encapsulated in a base class that is inheirited before `std::ostream`.
204
205 #include <boost/utility/base_from_member.hpp>
206
207 #include <streambuf> // for std::streambuf
208 #include <ostream> // for std::ostream
209
210 class fdoutbuf
211 : public std::streambuf
212 {
213 public:
214 explicit fdoutbuf( int fd );
215 //...
216 };
217
218 class fdostream
219 : private boost::base_from_member<fdoutbuf>
220 , public std::ostream
221 {
222 // Helper typedef's
223 typedef boost::base_from_member<fdoutbuf> pbase_type;
224 typedef std::ostream base_type;
225
226 public:
227 explicit fdostream( int fd )
228 : pbase_type( fd ), base_type( &member ){}
229 //...
230 };
231
232The base-from-member idiom is an implementation detail, so it should not
233be visible to the clients (or any derived classes) of `fdostream`. Due to
234the initialization order, the `fdoutbuf` sub-object will get initialized
235before the `std::ostream` sub-object does, making the former sub-object
236safe to use in the latter sub-object's construction. Since the `fdoutbuf`
237sub-object of the final type is the only sub-object with the name `member`
238that name can be used unqualified within the final class.
239
240[endsect]
241
242[section Example]
243
244The base-from-member class templates should commonly involve only one
245base-from-member sub-object, usually for attaching a stream-buffer to an
246I/O stream. The next example demonstrates how to use multiple
247base-from-member sub-objects and the resulting qualification issues.
248
249 #include <boost/utility/base_from_member.hpp>
250
251 #include <cstddef> /* for NULL */
252
253 struct an_int
254 {
255 int y;
256
257 an_int( float yf );
258 };
259
260 class switcher
261 {
262 public:
263 switcher();
264 switcher( double, int * );
265 //...
266 };
267
268 class flow_regulator
269 {
270 public:
271 flow_regulator( switcher &, switcher & );
272 //...
273 };
274
275 template < unsigned Size >
276 class fan
277 {
278 public:
279 explicit fan( switcher );
280 //...
281 };
282
283 class system
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
289 , public fan<6>
290 {
291 // Helper typedef's
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;
296
297 typedef flow_regulator base1_type;
298 typedef fan<6> base2_type;
299
300 public:
301 system( double x );
302 //...
303 };
304
305 system::system( double x )
306 : pbase0_type( 0.2 )
307 , pbase1_type()
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 )
312 {
313 //...
314 }
315
316The final class has multiple sub-objects with the name `member`, so any
317use of that name needs qualification by a name of the appropriate base
318type. (Using `typedef`s ease mentioning the base types.) However, the fix
319introduces a new problem when a pointer is needed. Using the address
320operator with a sub-object qualified with its class's name results in a
321pointer-to-member (here, having a type of `an_int boost::base_from_member<
322an_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.
325
326There are some argument conversions in the initialization. The constructor
327argument for `pbase0_type` is converted from `double` to `float`. The first
328constructor argument for `pbase2_type` is converted from `int` to `double`.
329The second constructor argument for `pbase3_type` is a special case of
330necessary conversion; all forms of the null-pointer literal in C++ (except
331`nullptr` from C++11) also look like compile-time integral expressions, so
332C++ always interprets such code as an integer when it has overloads that can
333take either an integer or a pointer. The last conversion is necessary for the
334compiler 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
336conversion if multiple pointer types can be accepted in a constructor call
337but `std::nullptr_t` cannot.)
338
339[endsect]
340
341[section Acknowledgments]
342
343* [@http://www.boost.org/people/ed_brey.htm Ed Brey] suggested some interface
344changes.
345
346* [@http://www.moocat.org R. Samuel Klatchko] ([@mailto:rsk@moocat.org
347rsk@moocat.org], [@mailto:rsk@brightmail.com rsk@brightmail.com]) invented
348the idiom of how to use a class member for initializing a base class.
349
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].
353
354* Jonathan Turkanis supplied an implementation of generating the constructor
355templates that can be controlled and automated with macros. The
356implementation uses the [@../../../preprocessor/index.html Preprocessor library].
357
358* [@http://www.boost.org/people/daryle_walker.html">Daryle Walker] started the
359library. Contributed the test file [@../../base_from_member_test.cpp
360base_from_member_test.cpp].
361
362[endsect]
363