mirror of
https://github.com/boostorg/utility.git
synced 2025-05-08 18:34:02 +00:00
377 lines
12 KiB
Plaintext
377 lines
12 KiB
Plaintext
[/
|
|
Copyright 2001, 2003, 2004, 2012 Daryle Walker.
|
|
Copyright (c) 2021, Alan Freitas
|
|
|
|
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
|
|
]
|
|
|
|
[section:base_from_member Base from Member]
|
|
[block'''<?dbhtml stop-chunking?>''']
|
|
|
|
[section Introduction]
|
|
|
|
The class templates __base_from_member__ support the base-from-member idiom.
|
|
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 <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. [@http://www.moocat.org R.
|
|
Samuel Klatchko] developed a way around this by using the initialization
|
|
order in his favor. Base classes are initialized 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.
|
|
|
|
|
|
[endsect]
|
|
|
|
[section 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 __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__<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
|
|
};
|
|
|
|
template < typename MemberType, int UniqueID >
|
|
class __base_from_member__<MemberType&, UniqueID>
|
|
{
|
|
protected:
|
|
MemberType& member;
|
|
|
|
explicit constexpr __base_from_member__( MemberType& x )
|
|
noexcept;
|
|
};
|
|
|
|
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.
|
|
|
|
A specialization for member references offers a single constructor taking
|
|
a `MemberType&`, which is the only way to initialize a reference.
|
|
|
|
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 Basic Usage]
|
|
|
|
With the starting example, the `fdoutbuf` sub-object needs to be
|
|
encapsulated in a base class that is inherited 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.
|
|
|
|
[endsect]
|
|
|
|
[section Multiple Sub-Objects]
|
|
|
|
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 `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]
|
|
|
|
[/===============]
|
|
[xinclude tmp/base_from_member_reference.xml]
|
|
[/===============]
|
|
|
|
[section Acknowledgments]
|
|
|
|
Author: Walker, Daryle
|
|
|
|
Copyright 2001, 2003, 2004, 2012 Daryle Walker
|
|
|
|
* [@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 [@boost:/libs/preprocessor/index.html Preprocessor library].
|
|
|
|
* [@http://www.boost.org/people/daryle_walker.html">Daryle Walker] started the
|
|
library. Contributed the test file [@../../../test/base_from_member_test.cpp
|
|
base_from_member_test.cpp].
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|