Introduction
The Boost.Core library is a collection of core utilities. The criteria for inclusion is that the utility component be:
- simple
- used by other Boost librarys, and
- not dependent on any other Boost modules except Core itself, Config, Assert, Static Assert, or Predef.
addressof
Overview
The header <boost/core/addressof.hpp>
defines the function template boost::addressof.boost::addressof(x)
returns the address of x.
Ordinarily, this address can be obtained by &x
, but the unary &
operator can be overloaded.
boost::addressof avoids calling used-defined operator&()
.
Synopsis
namespace boost
{
template<class T> T* addressof(T& x);
}
Example
#include<boost/core/addressof.hpp>
struct useless_type { };
class nonaddressable
{
useless_type operator& const;
};
void f()
{
nonaddressable x;
nonaddressable* xp = boost::addressof(x);
// nonaddressable* xpe = &x; /*error*/
}
Notes
In C++11 and above. boost::address is conditionally constexpr when possible. This is indicated by BOOST_CORE_NO_CONSTEXPR_ADDRESSOF not being defined.
With supported compilers, boost::addressof is always constexpr by leveraging compiler intrinsics. This is indicated by BOOST_CORE_HAS_BUILTIN_ADDRESSOF being defined.
checked_delete
Overview
The header <boost/checked_delete.hpp>
defines two function templates, checked_delete
and checked_array_delete
, and two class templates, checked_deleter
and checked_array_deleter
.
The C++ Standard allows, in 5.3.5/5, pointers to incomplete class types to be deleted with a delete-expression. When the class has a non-trivial destructor, or a class-specific operator delete, the behavior is undefined. Some compilers issue a warning when an incomplete type is deleted, but unfortunately, not all do, and programmers sometimes ignore or disable warnings.
A particularly troublesome case is when a smart pointer's destructor, such as boost::scoped_ptr<T>::~scoped_ptr
, is instantiated with an incomplete type. This can often lead to silent, hard to track failures.
The supplied function and class templates can be used to prevent these problems, as they require a complete type, and cause a compilation error otherwise.
Synopsis
namespace boost
{
template<class T> void checked_delete(T* p);
template<class T> void checked_array_delete(T* p);
template<class T> struct checked_deleter;
template<class T> struct checked_array_deleter;
}
checked_delete
template<class T>void checked_delete(T* p);
- Requires: T must be a complete type. The expression
delete p
must be well-formed. - Effects:
delete p;
checked_array_delete
template<class T> void checked_array_delete(T * p);
- Requires: T must be a complete type. The expression
delete [] p
must be well formed. - Effects:
delete [] p;
demangle
Overview
The header <boost/core/demangle.hpp>
defines several tools for undecorating symbol names.
Synopsis
namespace boost
{
namespace core
{
std::string demangle( char const * name );
char const * demangle_alloc( char const * name ) noexcept;
void demangle_free( char const * demangled_name ) noexcept;
class scoped_demangled_name
{
public:
explicit scoped_demangled_name( char const * name ) noexcept;
~scoped_demangled_name() noexcept;
char const * get() const noexcept;
scoped_demangled_name( scoped_demangled_name const& ) = delete;
scoped_demangled_name& operator= ( scoped_demangled_name const& ) = delete;
};
}
}
Conventional interface
The function boost::core::demangle
is the conventional way to obtain demangled symbol name. It takes a mangled string such as those returned by typeid(T).name()
on certain implementations such as g++
, and returns its demangled, human-readable, form. In case if demangling fails (e.g. if name cannot be interpreted as a mangled name) the function returns name.
Example
#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <iostream>
template<class T> struct X
{
};
int main()
{
char const * name = typeid( X<int> ).name();
std::cout << name << std::endl; // prints 1XIiE
std::cout << boost::core::demangle( name ) << std::endl; // prints X<int>
}
Low level interface
In some cases more low level interface may be desirable. For example:
- Assuming that symbol demangling may fail, the user wants to be able to handle such errors.
- The user needs to post-process the demangled name (e.g. remove common namespaces), and allocating a temporary string with the complete demangled name is significant overhead.
The function boost::core::demangle_alloc
performs name demangling and returns a pointer to a string with the demangled name, if succeeded, or nullptr otherwise. The returned pointer must be passed to boost::core::demangle_free
to reclaim resources. Note that on some platforms the pointer returned by boost::core::demangle_alloc
may refer to the string denoted by name, so this string must be kept immutable for the whole life time of the returned pointer.
The boost::core::scoped_demangled_name
class is a scope guard that automates the calls to boost::core::demangle_alloc
(on its construction) and boost::core::demangle_free
(on destruction). The string with the demangled name can be obtained with its get method. Note that this method may return nullptr
if demangling failed.
Example
#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <iostream>
template<class T> struct X
{
};
int main()
{
char const * name = typeid( X<int> ).name();
boost::core::scoped_demangled_name demangled( name );
std::cout << name << std::endl; // prints 1XIiE
std::cout << (demangled.get() ? demangled.get() : "[unknown]") << std::endl; // prints X<int>
}
enable_if
Overview
The enable_if
family of templates is a set of tools to allow a function template or a class template specialization to include or exclude itself from a set of matching functions or specializations based on properties of its template arguments. For example, one can define function templates that are only enabled for, and thus only match, an arbitrary set of types defined by a traits class. The enable_if templates can also be applied to enable class template specializations. Applications of enable_if are discussed in length in [1] and [2].
Synopsis
namespace boost {
template <class Cond, class T = void> struct enable_if;
template <class Cond, class T = void> struct disable_if;
template <class Cond, class T> struct lazy_enable_if;
template <class Cond, class T> struct lazy_disable_if;
template <bool B, class T = void> struct enable_if_c;
template <bool B, class T = void> struct disable_if_c;
template <bool B, class T> struct lazy_enable_if_c;
template <bool B, class T> struct lazy_disable_if_c;
}
Background
Sensible operation of template function overloading in C++ relies on the SFINAE (substitution-failure-is-not-an-error) principle [3]: if an invalid argument or return type is formed during the instantiation of a function template, the instantiation is removed from the overload resolution set instead of causing a compilation error. The following example, taken from [1], demonstrates why this is important:
int negate(int i) { return -i; }
template <class F>
typename F::result_type negate(const F& f) { return -f(); }
Suppose the compiler encounters the call negate(1)
. The first definition is obviously a better match, but the compiler must nevertheless consider (and instantiate the prototypes) of both definitions to find this out. Instantiating the latter definition with F
as int
would result in:
int::result_type negate(const int&);
where the return type is invalid. If this were an error, adding an unrelated function template (that was never called) could break otherwise valid code. Due to the SFINAE principle the above example is not, however, erroneous. The latter definition of negate
is simply removed from the overload resolution set.
The enable_if
templates are tools for controlled creation of the SFINAE conditions.
The enable_if templates
The names of the enable_if
templates have three parts: an optional lazy_
tag, either enable_if
or disable_if
, and an optional _c
tag. All eight combinations of these parts are supported. The meaning of the lazy_
tag is described in the section below. The second part of the name indicates whether a true condition argument should enable or disable the current overload. The third part of the name indicates whether the condition argument is a bool
value (_c
suffix), or a type containing a static bool
constant named value
(no suffix). The latter version interoperates with Boost.MPL.
The definitions of enable_if_c
and enable_if
are as follows (we use enable_if
templates unqualified but they are in the boost
namespace).
template <bool B, class T = void>
struct enable_if_c {
typedef T type;
};
template <class T>
struct enable_if_c<false, T> {};
template <class Cond, class T = void>
struct enable_if : public enable_if_c<Cond::value, T> {};
An instantiation of the enable_if_c
template with the parameter B
as true
contains a member type type
, defined to be T
. If B
is false, no such member is defined. Thus enable_if_c<B, T>::type
is either a valid or an invalid type expression, depending on the value of B
. When valid, enable_if_c<B, T>::type
equals T
. The enable_if_c
template can thus be used for controlling when functions are considered for overload resolution and when they are not. For example, the following function is defined for all arithmetic types (according to the classification of the Boost type_traits library):
template <class T>
typename enable_if_c<boost::is_arithmetic<T>::value, T>::type
foo(T t) { return t; }
The disable_if_c
template is provided as well, and has the same functionality as enable_if_c
except for the negated condition. The following function is enabled for all non-arithmetic types.
template <class T>
typename disable_if_c<boost::is_arithmetic<T>::value, T>::type
bar(T t) { return t; }
For easier syntax in some cases and interoperation with Boost.MPL we provide versions of the enable_if
templates taking any type with a bool
member constant named value
as the condition argument. The MPL bool_
, and_
, or_
, and not_
templates are likely to be useful for creating such types. Also, the traits classes in the Boost.Type_traits
library follow this convention. For example, the above example function foo
can be alternatively written as:
template <class T>
typename enable_if<boost::is_arithmetic<T>, T>::type
foo(T t) { return t; }
Using enable_if
The enable_if
templates are defined in boost/utility/enable_if.hpp
, which is included by boost/utility.hpp
.
With respect to function templates, enable_if
can be used in multiple different ways:
- As the return type of an instantiatied function
- As an extra parameter of an instantiated function
- As an extra template parameter (useful only in a compiler that supports
C++0x
default arguments for function template parameters, see Enabling function templates inC++0x
for details.
In the previous section, the return type form of enable_if was shown. As an example of using the form of enable_if that works via an extra function parameter, the foo function in the previous section could also be written as:
template <class T>
T foo(T t, typename enable_if<boost::is_arithmetic<T> >::type* dummy = 0);
Hence, an extra parameter of type void*
is added, but it is given a default value to keep the parameter hidden from client code. Note that the second template argument was not given to enable_if
, as the default void
gives the desired behavior.
Which way to write the enabler is largely a matter of taste, but for certain functions, only a subset of the options is possible:
- Many operators have a fixed number of arguments, thus enable_if must be used either in the return type or in an extra template parameter.
- Functions that have a variadic parameter list must use either the return type form or an extra template parameter.
- Constructors do not have a return type so you must use either an extra function parameter or an extra template parameter.
- Constructors that have a variadic parameter list must an extra template parameter.
- Conversion operators can only be written with an extra template parameter.
Enabling function templates in C++0x
In a compiler which supports C++0x default arguments for function template parameters, you can enable and disable function templates by adding an additional template parameter. This approach works in all situations where you would use either the return type form of enable_if or the function parameter form, including operators, constructors, variadic function templates, and even overloaded conversion operations.
As an example:
#include <boost/type_traits/is_arithmetic.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/utility/enable_if.hpp>
class test
{
public:
// A constructor that works for any argument list of size 10
template< class... T,
typename boost::enable_if_c< sizeof...( T ) == 10,
int >::type = 0>
test( T&&... );
// A conversion operation that can convert to any arithmetic type
template< class T,
typename boost::enable_if< boost::is_arithmetic< T >,
int >::type = 0>
operator T() const;
// A conversion operation that can convert to any pointer type
template< class T,
typename boost::enable_if< boost::is_pointer< T >,
int >::type = 0>
operator T() const;
};
int main()
{
// Works
test test_( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 );
// Fails as expected
test fail_construction( 1, 2, 3, 4, 5 );
// Works by calling the conversion operator enabled for arithmetic types
int arithmetic_object = test_;
// Works by calling the conversion operator enabled for pointer types
int* pointer_object = test_;
// Fails as expected
struct {} fail_conversion = test_;
}
Enabling template class specializations
Class template specializations can be enabled or disabled with enable_if
. One extra template parameter needs to be added for the enabler expressions. This parameter has the default value void
. For example:
template <class T, class Enable = void>
class A {...};
template <class T>
class A<T, typename enable_if<is_integral<T> >::type> {...};
template <claas T>
class A<T, typename enable_if<is_float<T> >::type> {...};
Instantiating A
with any integral type matches the first specialization, whereas any floating point type matches the second one. All other tklypes match the primary template. The condition can be an;y compile-time boolean expression that depends on the template arguments of the dependent value_type
from T
if and only if T::value_type
exists.