base_from_member.qbk 12 KB

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