From 6a1e97f870e52769d7c6b6eb97db0c40fe06bf1e Mon Sep 17 00:00:00 2001 From: K-ballo Date: Wed, 11 Jun 2014 17:34:37 -0300 Subject: [PATCH] Ported base_from_member documentation to Quickbook --- base_from_member.html | 398 --------------------------------------- doc/Jamfile.v2 | 19 ++ doc/base_from_member.qbk | 350 ++++++++++++++++++++++++++++++++++ index.html | 2 +- utility.htm | 6 +- 5 files changed, 373 insertions(+), 402 deletions(-) delete mode 100644 base_from_member.html create mode 100644 doc/base_from_member.qbk diff --git a/base_from_member.html b/base_from_member.html deleted file mode 100644 index 1466233..0000000 --- a/base_from_member.html +++ /dev/null @@ -1,398 +0,0 @@ - - - -Boost: Base-from-Member Idiom Documentation - - - -

C++ BoostBase-from-Member Idiom

- -

The class template boost::base_from_member provides -a workaround for a class that needs to initialize a base class with a -member. The class template is in boost/utility/base_from_member.hpp -which is included in boost/utility.hpp.

- -

There is test/example code in base_from_member_test.cpp.

- -

Contents

- - - -

Rationale

- -

When developing a class, sometimes a base class needs to be -initialized with a member of the current class. As a naïve -example:

- -
-#include <streambuf>  // for std::streambuf
-#include <ostream>    // for std::ostream
-
-class fdoutbuf
-    : public std::streambuf
-{
-public:
-    explicit fdoutbuf( int fd );
-    //...
-};
-
-class fdostream
-    : public std::ostream
-{
-protected:
-    fdoutbuf buf;
-public:
-    explicit fdostream( int fd )
-        : buf( fd ), std::ostream( &buf )
-        {}
-    //...
-};
-
- -

This is undefined because C++'s initialization order mandates that -the base class is initialized before the member it uses. R. Samuel Klatchko developed a way -around this by using the initialization order in his favor. Base -classes are intialized in order of declaration, so moving the desired -member to another base class, that is initialized before the desired -base class, can ensure proper initialization.

- -

A custom base class can be made for this idiom:

- -
-#include <streambuf>  // for std::streambuf
-#include <ostream>    // for std::ostream
-
-class fdoutbuf
-    : public std::streambuf
-{
-public:
-    explicit fdoutbuf( int fd );
-    //...
-};
-
-struct fdostream_pbase
-{
-    fdoutbuf sbuffer;
-
-    explicit fdostream_pbase( int fd )
-        : sbuffer( fd )
-        {}
-};
-
-class fdostream
-    : private fdostream_pbase
-    , public std::ostream
-{
-    typedef fdostream_pbase  pbase_type;
-    typedef std::ostream     base_type;
-
-public:
-    explicit fdostream( int fd )
-        : pbase_type( fd ), base_type( &sbuffer )
-        {}
-    //...
-};
-
- -

Other projects can use similar custom base classes. The technique -is basic enough to make a template, with a sample template class in -this library. The main template parameter is the type of the enclosed -member. The template class has several (explicit) constructor member -templates, which implicitly type the constructor arguments and pass them -to the member. The template class uses implicit copy construction and -assignment, cancelling them if the enclosed member is non-copyable.

- -

Manually coding a base class may be better if the construction -and/or copying needs are too complex for the supplied template class, -or if the compiler is not advanced enough to use it.

- -

Since base classes are unnamed, a class cannot have multiple (direct) -base classes of the same type. The supplied template class has an -extra template parameter, an integer, that exists solely to provide type -differentiation. This parameter has a default value so a single use of a -particular member type does not need to concern itself with the integer.

- -

Synopsis

- -
-#include <type_traits>  // exposition only
-
-#ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY
-#define BOOST_BASE_FROM_MEMBER_MAX_ARITY  10
-#endif
-
-template < typename MemberType, int UniqueID = 0 >
-class boost::base_from_member
-{
-protected:
-    MemberType  member;
-
-#if C++2011 is in use
-    template< typename ...T >
-    explicit constexpr   base_from_member( T&& ...x )
-     noexcept( std::is_nothrow_constructible<MemberType, T...>::value );
-#else
-    base_from_member();
-
-    template< typename T1 >
-    explicit  base_from_member( T1 x1 );
-
-    template< typename T1, typename T2 >
-    base_from_member( T1 x1, T2 x2 );
-
-    //...
-
-    template< typename T1, typename T2, typename T3, typename T4,
-     typename T5, typename T6, typename T7, typename T8, typename T9,
-     typename T10 >
-    base_from_member( T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7,
-     T8 x8, T9 x9, T10 x10 );
-#endif
-};
-
- -

The class template has a first template parameter -MemberType representing the type of the based-member. -It has a last template parameter UniqueID, that is an -int, to differentiate between multiple base classes that use -the same based-member type. The last template parameter has a default -value of zero if it is omitted. The class template has a protected -data member called member that the derived class can use -for later base classes (or itself).

- -

If the appropriate features of C++2011 are present, there will be a single -constructor template. It implements "perfect forwarding" to the best -constructor call of member (if any). The constructor template is -marked both constexpr and explicit. The former will -be ignored if the corresponding inner constructor call (of member) -does not have the marker. The latter binds the other way; always taking -effect, even when the inner constructor call does not have the marker. The -constructor template propagates the noexcept status of the inner -constructor call. (The constructor template has a trailing parameter with a -default value that disables the template when its signature is too close to the -signatures of the automatically-defined non-template copy- and/or -move-constructors of base_from_member.)

- -

On earlier-standard compilers, there is a default constructor and several -constructor member templates. These constructor templates can take as many -arguments (currently up to ten) as possible and pass them to a constructor of -the data member.

- -

Since C++ does not allow any way to explicitly state -the template parameters of a templated constructor, make sure that -the arguments are already close as possible to the actual type used in -the data member's desired constructor. Explicit conversions may be -necessary.

- -

The BOOST_BASE_FROM_MEMBER_MAX_ARITY macro constant specifies -the maximum argument length for the constructor templates. The constant -may be overridden if more (or less) argument configurations are needed. The -constant may be read for code that is expandable like the class template and -needs to maintain the same maximum size. (Example code would be a class that -uses this class template as a base class for a member with a flexible set of -constructors.) This constant is ignored when C++2011 features are present.

- -

Usage

- -

With the starting example, the fdoutbuf sub-object needs -to be encapsulated in a base class that is inheirited before -std::ostream.

- -
-#include <boost/utility/base_from_member.hpp>
-
-#include <streambuf>  // for std::streambuf
-#include <ostream>    // for std::ostream
-
-class fdoutbuf
-    : public std::streambuf
-{
-public:
-    explicit fdoutbuf( int fd );
-    //...
-};
-
-class fdostream
-    : private boost::base_from_member<fdoutbuf>
-    , public std::ostream
-{
-    // Helper typedef's
-    typedef boost::base_from_member<fdoutbuf>  pbase_type;
-    typedef std::ostream                        base_type;
-
-public:
-    explicit fdostream( int fd )
-        : pbase_type( fd ), base_type( &member )
-        {}
-    //...
-};
-
- -

The base-from-member idiom is an implementation detail, so it -should not be visible to the clients (or any derived classes) of -fdostream. Due to the initialization order, the -fdoutbuf sub-object will get initialized before the -std::ostream sub-object does, making the former -sub-object safe to use in the latter sub-object's construction. Since the -fdoutbuf sub-object of the final type is the only sub-object -with the name "member," that name can be used -unqualified within the final class.

- -

Example

- -

The base-from-member class templates should commonly involve -only one base-from-member sub-object, usually for attaching a -stream-buffer to an I/O stream. The next example demonstrates how -to use multiple base-from-member sub-objects and the resulting -qualification issues.

- -
-#include <boost/utility/base_from_member.hpp>
-
-#include <cstddef>  // for NULL
-
-struct an_int
-{
-    int  y;
-
-    an_int( float yf );
-};
-
-class switcher
-{
-public:
-    switcher();
-    switcher( double, int * );
-    //...
-};
-
-class flow_regulator
-{
-public:
-    flow_regulator( switcher &, switcher & );
-    //...
-};
-
-template < unsigned Size >
-class fan
-{
-public:
-    explicit fan( switcher );
-    //...
-};
-
-class system
-    : private boost::base_from_member<an_int>
-    , private boost::base_from_member<switcher>
-    , private boost::base_from_member<switcher, 1>
-    , private boost::base_from_member<switcher, 2>
-    , protected flow_regulator
-    , public fan<6>
-{
-    // Helper typedef's
-    typedef boost::base_from_member<an_int>       pbase0_type;
-    typedef boost::base_from_member<switcher>     pbase1_type;
-    typedef boost::base_from_member<switcher, 1>  pbase2_type;
-    typedef boost::base_from_member<switcher, 2>  pbase3_type;
-
-    typedef flow_regulator  base1_type;
-    typedef fan<6>          base2_type;
-
-public:
-    system( double x );
-    //...
-};
-
-system::system( double x )
-    : pbase0_type( 0.2 )
-    , pbase1_type()
-    , pbase2_type( -16, &this->pbase0_type::member.y )
-    , pbase3_type( x, static_cast<int *>(NULL) )
-    , base1_type( pbase3_type::member, pbase1_type::member )
-    , base2_type( pbase2_type::member )
-{
-    //...
-}
-
- -

The final class has multiple sub-objects with the name -"member," so any use of that name needs qualification by -a name of the appropriate base type. (Using typedefs -ease mentioning the base types.) However, the fix introduces a new -problem when a pointer is needed. Using the address operator with -a sub-object qualified with its class's name results in a pointer-to-member -(here, having a type of an_int boost::base_from_member<an_int, -0> :: *) instead of a pointer to the member (having a type of -an_int *). The new problem is fixed by qualifying the -sub-object with "this->," and is needed just -for pointers, and not for references or values.

- -

There are some argument conversions in the initialization. The -constructor argument for pbase0_type is converted from -double to float. The first constructor -argument for pbase2_type is converted from int -to double. The second constructor argument for -pbase3_type is a special case of necessary conversion; all -forms of the null-pointer literal in C++ (except nullptr from -C++2011) also look like compile-time integral expressions, so C++ always -interprets such code as an integer when it has overloads that can take either -an integer or a pointer. The last conversion is necessary for the compiler to -call a constructor form with the exact pointer type used in -switcher's constructor. (If C++2011's nullptr is -used, it still needs a conversion if multiple pointer types can be accepted in -a constructor call but std::nullptr_t cannot.)

- -

Credits

- -

Contributors

- -
-
Ed Brey -
Suggested some interface changes. - -
R. Samuel Klatchko (rsk@moocat.org, rsk@brightmail.com) -
Invented the idiom of how to use a class member for initializing - a base class. - -
Dietmar Kuehl -
Popularized the base-from-member idiom in his - IOStream - example classes. - -
Jonathan Turkanis -
Supplied an implementation of generating the constructor templates that - can be controlled and automated with macros. The implementation uses - the Preprocessor library. - -
Daryle Walker -
Started the library. Contributed the test file base_from_member_test.cpp. -
- -
- -

Revised: 16 February 2012

- -

Copyright 2001, 2003, 2004, 2012 Daryle Walker. Use, modification, and distribution -are subject to the Boost Software License, Version 1.0. (See accompanying -file LICENSE_1_0.txt or a copy at <http://www.boost.org/LICENSE_1_0.txt>.)

- - - diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 3746a82..813697e 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -42,6 +42,25 @@ using quickbook ; path-constant boost-images : ../../../doc/src/images ; +xml base_from_member : base_from_member.qbk ; +boostbook standalone_base_from_member + : + base_from_member + : + # File name of HTML output: + root.filename=base_from_member + # How far down we chunk nested sections, basically all of them: + chunk.section.depth=0 + # Don't put the first section on the same page as the TOC: + chunk.first.sections=0 + # How far down sections get TOC's + toc.section.depth=1 + # Max depth in each TOC: + toc.max.depth=1 + # How far down we go with TOC's + generate.section.toc.level=1 + ; + xml declval : declval.qbk ; boostbook standalone_declval : diff --git a/doc/base_from_member.qbk b/doc/base_from_member.qbk new file mode 100644 index 0000000..0a4f568 --- /dev/null +++ b/doc/base_from_member.qbk @@ -0,0 +1,350 @@ +[/ + Copyright 2001, 2003, 2004, 2012 Daryle Walker. + + Distributed under the Boost Software License, Version 1.0. + + See accompanying file LICENSE_1_0.txt + or copy at http://boost.org/LICENSE_1_0.txt +] + +[article Base_From_Member + [quickbook 1.5] + [authors [Walker, Daryle]] + [copyright 2001, 2003, 2004, 2012 Daryle Walker] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + +[section Rationale] + +When developing a class, sometimes a base class needs to be initialized +with a member of the current class. As a na\u00EFve example: + + #include /* for std::streambuf */ + #include /* for std::ostream */ + + class fdoutbuf + : public std::streambuf + { + public: + explicit fdoutbuf( int fd ); + //... + }; + + class fdostream + : public std::ostream + { + protected: + fdoutbuf buf; + public: + explicit fdostream( int fd ) + : buf( fd ), std::ostream( &buf ) {} + //... + }; + +This is undefined because C++'s initialization order mandates that the base +class is initialized before the member it uses. [@http://www.moocat.org R. +Samuel Klatchko] developed a way around this by using the initialization +order in his favor. Base classes are intialized in order of declaration, so +moving the desired member to another base class, that is initialized before +the desired base class, can ensure proper initialization. + +A custom base class can be made for this idiom: + + #include /* for std::streambuf */ + #include /* for std::ostream */ + + class fdoutbuf + : public std::streambuf + { + public: + explicit fdoutbuf( int fd ); + //... + }; + + struct fdostream_pbase + { + fdoutbuf sbuffer; + + explicit fdostream_pbase( int fd ) + : sbuffer( fd ) {} + }; + + class fdostream + : private fdostream_pbase + , public std::ostream + { + typedef fdostream_pbase pbase_type; + typedef std::ostream base_type; + + public: + explicit fdostream( int fd ) + : pbase_type( fd ), base_type( &sbuffer ) {} + //... + }; + +Other projects can use similar custom base classes. The technique is basic +enough to make a template, with a sample template class in this library. +The main template parameter is the type of the enclosed member. The +template class has several (explicit) constructor member templates, which +implicitly type the constructor arguments and pass them to the member. The +template class uses implicit copy construction and assignment, cancelling +them if the enclosed member is non-copyable. + +Manually coding a base class may be better if the construction and/or +copying needs are too complex for the supplied template class, or if the +compiler is not advanced enough to use it. + +Since base classes are unnamed, a class cannot have multiple (direct) base +classes of the same type. The supplied template class has an extra template +parameter, an integer, that exists solely to provide type differentiation. +This parameter has a default value so a single use of a particular member +type does not need to concern itself with the integer. + +[endsect] + +[section Synopsis] + + #include /* exposition only */ + + #ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY + #define BOOST_BASE_FROM_MEMBER_MAX_ARITY 10 + #endif + + template < typename MemberType, int UniqueID = 0 > + class boost::base_from_member + { + protected: + MemberType member; + + #if ``['C++11 is in use]`` + template< typename ...T > + explicit constexpr base_from_member( T&& ...x ) + noexcept( std::is_nothrow_constructible::value ); + #else + base_from_member(); + + template< typename T1 > + explicit base_from_member( T1 x1 ); + + template< typename T1, typename T2 > + base_from_member( T1 x1, T2 x2 ); + + //... + + template< typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7, typename T8, typename T9, + typename T10 > + base_from_member( T1 x1, T2 x2, T3 x3, T4 x4, T5 x5, T6 x6, T7 x7, + T8 x8, T9 x9, T10 x10 ); + #endif + }; + +The class template has a first template parameter `MemberType` representing +the type of the based-member. It has a last template parameter `UniqueID`, +that is an `int`, to differentiate between multiple base classes that use +the same based-member type. The last template parameter has a default value +of zero if it is omitted. The class template has a protected data member +called `member` that the derived class can use for later base classes (or +itself). + +If the appropriate features of C++11 are present, there will be a single +constructor template. It implements ['perfect forwarding] to the best +constructor call of `member` (if any). The constructor template is marked +both `constexpr` and `explicit`. The former will be ignored if the +corresponding inner constructor call (of `member`) does not have the marker. +The latter binds the other way; always taking effect, even when the inner +constructor call does not have the marker. The constructor template +propagates the `noexcept` status of the inner constructor call. (The +constructor template has a trailing parameter with a default value that +disables the template when its signature is too close to the signatures of +the automatically-defined non-template copy- and/or move-constructors of +`base_from_member`.) + +On earlier-standard compilers, there is a default constructor and several +constructor member templates. These constructor templates can take as many +arguments (currently up to ten) as possible and pass them to a constructor +of the data member. + +Since C++ does not allow any way to explicitly state the template parameters +of a templated constructor, make sure that the arguments are already close +as possible to the actual type used in the data member's desired constructor. +Explicit conversions may be necessary. + +The `BOOST_BASE_FROM_MEMBER_MAX_ARITY` macro constant specifies the maximum +argument length for the constructor templates. The constant may be overridden +if more (or less) argument configurations are needed. The constant may be +read for code that is expandable like the class template and needs to +maintain the same maximum size. (Example code would be a class that uses +this class template as a base class for a member with a flexible set of +constructors.) This constant is ignored when C++11 features are present. + +[endsect] + +[section Usage] + +With the starting example, the `fdoutbuf` sub-object needs to be +encapsulated in a base class that is inheirited before `std::ostream`. + + #include + + #include // for std::streambuf + #include // for std::ostream + + class fdoutbuf + : public std::streambuf + { + public: + explicit fdoutbuf( int fd ); + //... + }; + + class fdostream + : private boost::base_from_member + , public std::ostream + { + // Helper typedef's + typedef boost::base_from_member pbase_type; + typedef std::ostream base_type; + + public: + explicit fdostream( int fd ) + : pbase_type( fd ), base_type( &member ){} + //... + }; + +The base-from-member idiom is an implementation detail, so it should not +be visible to the clients (or any derived classes) of `fdostream`. Due to +the initialization order, the `fdoutbuf` sub-object will get initialized +before the `std::ostream` sub-object does, making the former sub-object +safe to use in the latter sub-object's construction. Since the `fdoutbuf` +sub-object of the final type is the only sub-object with the name `member` +that name can be used unqualified within the final class. + +[endsect] + +[section Example] + +The base-from-member class templates should commonly involve only one +base-from-member sub-object, usually for attaching a stream-buffer to an +I/O stream. The next example demonstrates how to use multiple +base-from-member sub-objects and the resulting qualification issues. + + #include + + #include /* for NULL */ + + struct an_int + { + int y; + + an_int( float yf ); + }; + + class switcher + { + public: + switcher(); + switcher( double, int * ); + //... + }; + + class flow_regulator + { + public: + flow_regulator( switcher &, switcher & ); + //... + }; + + template < unsigned Size > + class fan + { + public: + explicit fan( switcher ); + //... + }; + + class system + : private boost::base_from_member + , private boost::base_from_member + , private boost::base_from_member + , private boost::base_from_member + , protected flow_regulator + , public fan<6> + { + // Helper typedef's + typedef boost::base_from_member pbase0_type; + typedef boost::base_from_member pbase1_type; + typedef boost::base_from_member pbase2_type; + typedef boost::base_from_member pbase3_type; + + typedef flow_regulator base1_type; + typedef fan<6> base2_type; + + public: + system( double x ); + //... + }; + + system::system( double x ) + : pbase0_type( 0.2 ) + , pbase1_type() + , pbase2_type( -16, &this->pbase0_type::member.y ) + , pbase3_type( x, static_cast(NULL) ) + , base1_type( pbase3_type::member, pbase1_type::member ) + , base2_type( pbase2_type::member ) + { + //... + } + +The final class has multiple sub-objects with the name `member`, so any +use of that name needs qualification by a name of the appropriate base +type. (Using `typedef`s ease mentioning the base types.) However, the fix +introduces a new problem when a pointer is needed. Using the address +operator with a sub-object qualified with its class's name results in a +pointer-to-member (here, having a type of `an_int boost::base_from_member< +an_int, 0> :: *`) instead of a pointer to the member (having a type of +`an_int *`). The new problem is fixed by qualifying the sub-object with +`this->` and is needed just for pointers, and not for references or values. + +There are some argument conversions in the initialization. The constructor +argument for `pbase0_type` is converted from `double` to `float`. The first +constructor argument for `pbase2_type` is converted from `int` to `double`. +The second constructor argument for `pbase3_type` is a special case of +necessary conversion; all forms of the null-pointer literal in C++ (except +`nullptr` from C++11) also look like compile-time integral expressions, so +C++ always interprets such code as an integer when it has overloads that can +take either an integer or a pointer. The last conversion is necessary for the +compiler to call a constructor form with the exact pointer type used in +`switcher`'s constructor. (If C++11's `nullptr` is used, it still needs a +conversion if multiple pointer types can be accepted in a constructor call +but `std::nullptr_t` cannot.) + +[endsect] + +[section Acknowledgments] + +* [@http://www.boost.org/people/ed_brey.htm Ed Brey] suggested some interface +changes. + +* [@http://www.moocat.org R. Samuel Klatchko] ([@mailto:rsk@moocat.org +rsk@moocat.org], [@mailto:rsk@brightmail.com rsk@brightmail.com]) invented +the idiom of how to use a class member for initializing a base class. + +* [@http://www.boost.org/people/dietmar_kuehl.htm Dietmar Kuehl] popularized the + base-from-member idiom in his [@http://www.informatik.uni-konstanz.de/~kuehl/c++/iostream/ + IOStream example classes]. + +* Jonathan Turkanis supplied an implementation of generating the constructor +templates that can be controlled and automated with macros. The +implementation uses the [@../../../preprocessor/index.html Preprocessor library]. + +* [@http://www.boost.org/people/daryle_walker.html">Daryle Walker] started the +library. Contributed the test file [@../../base_from_member_test.cpp +base_from_member_test.cpp]. + +[endsect] + diff --git a/index.html b/index.html index f5fe62b..04cc305 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,7 @@

addressof
- base_from_member
+ base_from_member
BOOST_BINARY
call_traits
checked_delete
diff --git a/utility.htm b/utility.htm index 5635d48..8096b40 100644 --- a/utility.htm +++ b/utility.htm @@ -11,8 +11,8 @@

Contents