From e763315b55ad2afdfee5712ba1ef417a571a0ec8 Mon Sep 17 00:00:00 2001
From: Daryle Walker
@@ -166,13 +174,27 @@ 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). -+#include <type_traits> // exposition only + #ifndef BOOST_BASE_FROM_MEMBER_MAX_ARITY #define BOOST_BASE_FROM_MEMBER_MAX_ARITY 10 #endif @@ -139,6 +141,11 @@ 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 > @@ -154,6 +161,7 @@ protected: 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 };
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 +
If the variadic template arguments and r-value reference 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.
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.
+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 @@ -180,7 +202,7 @@ 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.)
+constructors.) This constant is ignored when C++2011 features are present.pbase0_type
is converted from
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++ 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.
+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.)
switcher
's constructor.
Revised: 28 August 2004
+Revised: 11 February 2012
-Copyright 2001, 2003, 2004 Daryle Walker. Use, modification, and distribution +
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/include/boost/utility/base_from_member.hpp b/include/boost/utility/base_from_member.hpp index 04aabb5..8148077 100644 --- a/include/boost/utility/base_from_member.hpp +++ b/include/boost/utility/base_from_member.hpp @@ -1,6 +1,6 @@ // boost utility/base_from_member.hpp header file --------------------------// -// Copyright 2001, 2003, 2004 Daryle Walker. Use, modification, and +// 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 //\\1 | \\2 |
Copyright © 2009-2012 Lorenzo + Caminiti
+ Distributed under 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) +
Table of Contents
+ This library allows to wrap type expressions within round parenthesis so they + can be passed to macros even when they contain commas. +
+ Consider the following macro which declares a variable named var
n with the specified
+ type (see also var_err.cpp
):
+
+
#define VAR(type, n) type var ## n + +VAR( int, 1 ); // OK. +VAR( std::map<int, char>, 2 ); // Error. +
+
+ The first macro invocation works correctly declaring a variable named var1
of type int
.
+ However, the second macro invocation fails generating a preprocessor error
+ similar to the following:
+
error: macro "VAR" passed 3 arguments, but takes just 2 +
+ That is because the std::map
type passed as the first macro parameter
+ contains a comma ,
not wrapped
+ by round parenthesis ()
. The preprocessor
+ interprets that unwrapped comma as a separation between macro parameters concluding
+ that a total of three (and not two) parameters are passed to the macro in the
+ following order:
+
std::map<int
+ char>
+ 2
+
+ Note that, differently from the compiler, the preprocessor only recognizes
+ round parameters ()
. Angular
+ <>
or squared []
parenthesis are not used by the preprocessor
+ when parsing the macro parameters.
+
+ In some cases, it might be possible to workaround this issue by avoiding to
+ pass the type expression to the macro all together. For example, in some cases
+ a typedef
can be used to specify
+ the type expression with the commas outside the macro (see also var.cpp
):
+
+
typedef std::map<int, char> map_type; +VAR( map_type, 3 ); // OK. +
+
+ When this is not possible or desired (e.g., see the function template f
in the section below), the library header
+ boost/utility/identity_type.hpp
+ defines a macro BOOST_IDENTITY_TYPE
+ which can be used to workaround the issue while keeping the type expression
+ as one of the macro parameters (see also var.cpp
).
+
+
#include <boost/utility/identity_type.hpp> + +VAR( BOOST_IDENTITY_TYPE((std::map<int, char>)), 4 ); // OK. +
+
+ This macro expands to an expression that evaluates (at compile-time) to the
+ specified type. The specified type is never split into multiple macro parameters
+ because it is always wrapped by a set of extra round parenthesis ()
. In fact, a total of two sets of round parenthesis
+ must be used: The parenthesis to invoke the macro BOOST_IDENTITY_TYPE(...)
plus the inner parenthesis to wrap the
+ type passed to the macro BOOST_IDENTITY_TYPE((...))
.
+
+ This macro works on any C++03 + compiler (because it does not use variadic + macros). [1] +
+ This macro must be prefixed by typename
+ when used within templates. For example, let's program a macro that declares
+ a function parameter named arg
n
+ with the specified type (see also template.cpp
):
+
+
#define ARG(type, n) type arg ## n + +template<typename T> +void f( // Prefix macro with `typename` in templates. + ARG( typename BOOST_IDENTITY_TYPE((std::map<int, T>)), 1 ) +) { + std::cout << arg1[0] << std::endl; +} +
+
+
std::map<int, char> a; +a[0] = 'a'; + +f<char>(a); // OK. +// f(a); // But error. +
+
+ However, note that the template parameter type char
+ must be explicitly specified when invoking the function f<char>(a)
. In fact,
+ when the BOOST_IDENTITY_TYPE
+ macro is used to wrap a function template parameter, the template parameter
+ can no longer be implicitly determined by the compiler form the function call
+ as in f(a)
. (This
+ limitation does not apply to class templates because class template parameters
+ must always be explicitly specified.) In other words, without using the BOOST_IDENTITY_TYPE
macro, C++ would
+ normally be able to implicitly deduce the function template parameter as shown
+ below:
+
+
template<typename T> +void g( + std::map<int, T> arg1 +) { + std::cout << arg1[0] << std::endl; +} +
+
+
g<char>(a); // OK. +g(a); // Also OK. +
+
+ On some compilers (e.g., GCC), using this macro on abstract types (i.e., a + class with one or more pure virtual functions) generates a compiler error. + This can be worked around by manipulating the type adding and removing a reference + to it. +
+ Let's program a macro that performs a static assertion on a Template
+ Meta-Programming (TMP) meta-function (similarly to Boost.MPL BOOST_MPL_ASSERT
). The BOOST_IDENTITY_TYPE
macro can be used
+ to pass a meta-function with multiple template parameters to the assert macro
+ (so to handle the commas separating the template parameters). In this case,
+ if the meta-function is an abstract type, it needs to be manipulated adding
+ and removing a reference to it (see also tmp_assert.cpp
):
+
+
#define TMP_ASSERT(metafunction) \ + BOOST_STATIC_ASSERT(metafunction::value) + +template<typename T, bool b> +struct abstract { + static const bool value = b; + virtual void f(T const& x) = 0; +}; + +TMP_ASSERT( + boost::remove_reference< // Add and remove + BOOST_IDENTITY_TYPE(( // reference for + boost::add_reference< // abstract type. + abstract<int, true> + >::type + )) + >::type +); +
+
+ The BOOST_IDENTITY_TYPE
macro
+ can be used either in the call of the user-defined macro (as shown by the examples
+ so far), or internally to the definition of the user macro. When BOOST_IDENTITY_TYPE
is used internally,
+ the call of the user macro will only have to specify the extra parenthesis
+ (see also tmp_assert.cpp
):
+
+
#define TMP_ASSERT_PAREN(parenthesized_metafunction) \ + /* use `BOOST_IDENTITY_TYPE` in macro definition instead of invocation */ \ + BOOST_STATIC_ASSERT(BOOST_IDENTITY_TYPE(parenthesized_metafunction)::value) + +TMP_ASSERT_PAREN(( boost::is_const<std::map<int, char> const> )); +TMP_ASSERT( BOOST_IDENTITY_TYPE((boost::is_const<std::map<int, char> const>)) ); +
+
+ However, note that the user will always have to specify + the extra parenthesis even when the macro parameters contain no comma: +
+
TMP_ASSERT_PAREN(( boost::is_const<int const> )); // Always extra `()`. +TMP_ASSERT( boost::is_const<int const> ); // No extra `()` and no macro. +
+
+ In some cases, using BOOST_IDENTITY_TYPE
+ internally might provide the best syntax for the user. For example, this is
+ the case for BOOST_MPL_ASSERT
+ because the majority of template meta-programming expressions contain unwrapped
+ commas so it is less confusing for the user to always specify the extra parenthesis
+ ((...))
instead of using BOOST_IDENTITY_TYPE
:
+
BOOST_MPL_ASSERT(( // Natural syntax. + boost::mpl::and_< + boost::is_const<T> + , boost::is_reference<T> + > +)); +
+ However, in other situations it might be preferable to not require the extra
+ parenthesis in common cases and handle commas as special cases using the BOOST_IDENTITY_TYPE
. For example, this
+ is the case for BOOST_LOCAL_FUNCTION
+ for which always requiring the extra parenthesis ((...))
+ around the types would lead to an unnatural syntax for the local function parameter
+ types:
+
int BOOST_LOCAL_FUNCTION( ((int&)) x, ((int&)) y ) { // Unnatural syntax. + return x + y; +} BOOST_LOCAL_FUNCTION_NAME(add) +
+ Instead requiring the user to specify BOOST_IDENTITY_TYPE
+ when needed allows for the more natural syntax BOOST_LOCAL_FUNCTION(int&
+ x, int& y)
in the common cases when the parameter types
+ contain no comma.
+
+ The implementation of this library macro is equivalent to the following: [2] +
#include <boost/type_traits/function_traits.hpp> + +#define BOOST_IDENTITY_TYPE(parenthesized_type) \ + boost::function_traits<void parenthesized_type>::arg1_type +
+ Essentially, the type is wrapped between round parenthesis (std::map<int,
+ char>)
+ so it can be passed as a macro parameter even if it contain commas. Then the
+ parenthesized type is transformed into the type of a function returning void
and with the specified type as the type
+ of the first and only argument void
+ (std::map<int, char>)
. Finally, the type of the first argument
+ arg1_type
is extracted therefore
+ obtaining the original type from the parenthesized type (effectively stripping
+ the parenthesis from around the type).
+
Wrap type expressions with round parenthesis so they can be passed to macros even if they contain commas.
+ +BOOST_IDENTITY_TYPE(parenthesized_type)
BOOST_IDENTITY_TYPE — This macro allows to wrap the specified type expression within extra round parenthesis so the type can be passed as a single macro parameter even if it contains commas (not already wrapped within round parenthesis).
// In header: <boost/utility/identity_type.hpp>
+
+BOOST_IDENTITY_TYPE(parenthesized_type)
Parameters:
parenthesized_type | The type expression to be passed as macro parameter wrapped by a single set of round parenthesis (...) . This type expression can contain an arbitrary number of commas. |
+
This macro works on any C++03 compiler (it does not require variadic macros).
This macro must be prefixed by typename
when used within templates. However, the compiler will not be able to automatically determine function template parameters when they are wrapped with this macro (these parameters need to be explicitly specified when calling the function template).
On some compilers (like GCC), using this macro on an abstract types requires to add and remove a reference to the type.
[1]
+ Using variadic macros, it would be possible to use a single set of parenthesis
+ BOOST_IDENTITY_TYPE(
type)
instead of two BOOST_IDENTITY_TYPE((
type))
+ but variadic macros are not part of C++03 (even if nowadays they are supported
+ by most modern compilers and they are also part of C++11).
+
[2] + There is absolutely no guarantee that the macro is actually implemented using + the code listed in this documentation. This code is for explanatory purposes + only. +
© Copyright Lorenzo Caminiti, 2009-2012
+Distributed under the Boost Software License, Version 1.0 (see + accompanying file + LICENSE_1_0.txt or a copy at + www.boost.org/LICENSE_1_0.txt)
+ + diff --git a/identity_type/test/Jamfile.v2 b/identity_type/test/Jamfile.v2 new file mode 100644 index 0000000..2f0bdac --- /dev/null +++ b/identity_type/test/Jamfile.v2 @@ -0,0 +1,14 @@ + +# Copyright (C) 2009-2012 Lorenzo Caminiti +# Distributed under 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) +# Home at http://www.boost.org/libs/utility/identity_type + +import testing ; + +compile-fail var_err.cpp ; +run var.cpp ; +run template.cpp ; +run tmp_assert.cpp ; + diff --git a/identity_type/test/template.cpp b/identity_type/test/template.cpp new file mode 100644 index 0000000..ec42628 --- /dev/null +++ b/identity_type/test/template.cpp @@ -0,0 +1,48 @@ + +// Copyright (C) 2009-2012 Lorenzo Caminiti +// Distributed under 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) +// Home at http://www.boost.org/libs/utility/identity_type + +#include