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 [@../../test/base_from_member_test.cpp 360base_from_member_test.cpp]. 361 362[endsect] 363 364