boost learning note (2) Boost.Core

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 in C++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 Tif and only if T::value_type exists.

Pages