Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

Other considerations and tips

Native typeof support and emulation
The three participating parties
Supported features
What needs to be registered?
Limitations

Native typeof support and emulation

Many compilers support typeof already, most noticeable GCC and Metrowerks.

Igor Chesnokov discovered a method that allows to implement typeof on the VC series of compilers. It uses a bug in the Microsoft compiler that allows a nested class of base to be defined in a class derived from base:

template<int ID> struct typeof_access
{
    struct id2type; //not defined
};

template<class T, int ID> struct typeof_register : typeof_access
{
    // define base's nested class here
    struct typeof_access::id2type
    {
        typedef T type;
    };
};

//Type registration function 
typeof_register<T, compile-time-constant> register_type(const T&);

//Actually register type by instantiating typeof_register for the correct type
sizeof(register_type(some-type));

//Use the base class to access the type.
typedef typeof_access::id2type::type type;

Peder Holt adapted this method to VC7.0, where the nested class is a template class that is specialized in the derived class.

In VC8.0, it seemed that all the bug-featire had been fixed, but Steven Watanabe managed to implement a more rigorous version of the VC7.0 fix that enables 'typeof' to be supported 'natively' here as well.

For many other compilers neither native typeof support nor the trick described above is an option. For such compilers the emulation method is the only way of implementing typeof.

According to a rough estimate, at the time of this writing the introduction of the typeof, auto, etc., into the C++ standard may not happen soon. Even after it's done, some time still has to pass before most compilers implement this feature. But even after that, there always are legacy compilers to support (for example now, in 2005, many people are still using VC6, long after VC7.x, and even VC8.0 beta became available).

Considering extreme usefulness of the feature right now, it seems to make sense to implement it at the library level.

The emulation mode seems to be important even if a better option is present on some particular compiler. If a library author wants to develop portable code using typeof, she needs to use emulation mode and register her types and templates. Those users who have a better option can still take advantage of it, since the registration macros are defined as no-op on such compilers, while the users for whom emulation is the only option will use it.

The other consideration applies to the users of VC7.1. Even though the more convenient typeof trick is available, the possibility of upgrade to VC8, where emulation remains the only option, should be considered.

The emulation mode can be forced on the compilers that don't use it by default by defining the BOOST_TYPEOF_COMPLIANT symbol:

g++ -D BOOST_TYPEOF_COMPLIANT -I \boost\boost_1_32_0 main.cpp

The three participating parties

The Lambda example from the Motivation section requires the following registration:

#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()

BOOST_TYPEOF_REGISTER_TEMPLATE(boost::tuples::tuple, 2);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::lambda_functor, 1);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::lambda_functor_base, 2);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::relational_action, 1);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::logical_action, 1);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::other_action, 1);
BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::greater_action);
BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::less_action);
BOOST_TYPEOF_REGISTER_TYPE(boost::lambda::and_action);
BOOST_TYPEOF_REGISTER_TEMPLATE(boost::lambda::placeholder, (int));

It may seem that the price for the ability to discover the expression's type is too high: rather large amount of registration is required. However note that all of the above registration is done only once, and after that, any combination of the registered types and templates would be handled. Moreover, this registration is typically done not by the end-user, but rather by a layer on top of some library (in this example -- Boost.Lambda).

When thinking about this, it's helpful to consider three parties: the typeof facility, the library (probably built on expression templates principle), and the end-user. The typeof facility is responsible for registering fundamental types. The library can register its own types and templates.

In the best-case scenario, if the expressions always consist of only fundamental types and library-defined types and templates, a library author can achieve the impression that the typeof is natively supported for her library. On the other hand, the more often expressions contain user-defined types, the more responsibility is put on the end-user, and therefore the less attractive this approach becomes.

Thus, the ratio of user-defined types in the expressions should be the main factor to consider when deciding whether or not to apply the typeof facility.

Supported features

The Typeof library pre-registers fundamental types. For these types, and for any other types/templates registered by the user library or end-user, any combination of the following is supported:

  • Pointers;
  • References (except top-level);
  • Consts (except top-level);
  • Volatiles (except top-level);
  • Arrays;
  • Functions, function pointers, and references;
  • Pointers to member functions;
  • Pointers to data members.

For example the following type:

int& (*)(const volatile char*, double[5], void(*)(short))

is supported right away, and something like:

void (MyClass::*)(int MyClass::*, MyClass[10]) const

is supported provided MyClass is registered.

The Typeof Library also provides registration files for most STL classes/templates. These files are located in the std subdirectory, and named after corresponding STL headers. These files are not included by the typeof system and have to be explicitly included by the user, as needed:

#include <boost/typeof/std/functional.hpp>
BOOST_AUTO(fun, std::bind2nd(std::less<int>(), 21)); //create named function object for future use.

What needs to be registered?

It is possible to take advantage of the compiler when registering types for the Typeof Library. Even though there is currently no direct support for typeof in the language, the compiler is aware of what the type of an expression is, and gives an error if it encounters an expression that has not been handled correctly. In the typeof context, this error message will contain clues to what types needs to be registered with the Typeof Library in order for BOOST_TYPEOF to work.

struct X {};

template<typename A,bool B>
struct Y {};

std::pair<X,Y<int,true> > a;

BOOST_AUTO(a,b);

We get the following error message from VC7.1

error C2504: 'boost::type_of::'anonymous-namespace'::encode_type_impl<V,Type_Not_Registered_With_Typeof_System>' : base
    class undefined
        with
        [
            V=boost::type_of::'anonymous-namespace'::encode_type_impl<boost::mpl::vector0<boost::mpl::na>,std::pair<X,Y<int,true>>>::V0,
            Type_Not_Registered_With_Typeof_System=X
        ]

Inspecting this error message, we see that the compiler complains about X

BOOST_TYPEOF_REGISTER_TYPE(X); //register X with the typeof system

Recompiling, we get a new error message from VC7.1

error C2504: 'boost::type_of::'anonymous-namespace'::encode_type_impl<V,Type_Not_Registered_With_Typeof_System>' : base
    class undefined
        with
        [
            V=boost::type_of::'anonymous-namespace'::encode_type_impl<boost::mpl::vector0<boost::mpl::na>,std::pair<X,Y<int,true>>>::V1,
            Type_Not_Registered_With_Typeof_System=Y<int,true>
        ]

Inspecting this error message, we see that the compiler complains about Y<int,true>. Since Y is a template, and contains integral constants, we need to take more care when registering:

BOOST_TYPEOF_REGISTER_TEMPLATE(Y,(typename)(bool)); //register template class Y

It is a good idea to look up the exact definition of Y when it contains integral constants. For simple template classes containing only typenames, you can rely solely on the compiler error.

The above code now compiles.

This technique can be used to get an overview of which types needs to be registered for a given project in order to support typeof.

Limitations

Nested template template parameters are not supported, like:

template<template<template<class> class> class Tpl>
class A; // can't register!

Classes and templates nested inside other templates also can't be registered because of the issue of nondeduced context. This limitation is most noticeable with regards to standard iterators in Dinkumware STL, which are implemented as nested classes. Instead, instantiations can be registered:

BOOST_TYPEOF_REGISTER_TYPE(std::list<int>::const_iterator)

PrevUpHomeNext