From: Vertleyb (vertleyb_at_[hidden])
Date: 2004-07-22 00:00:46
Recently there was a discussion about a typeof emulation library. The
library determines the type of an expression by passing it to a function
template, encoding the type as a list of integers, "returning" them
one-by-one via sizeof(), and decoding the type.
One of the main problems of any such emulation is the need to associate
types and templates with unique compile-time integral identifiers. This
becomes even bigger problem, when expressions can contain types/templates
from different libraries. It's not quite clear how to ensure uniqueness of
identifiers in this case (GUID would be an option, but it's far too big to
fit into an integer).
An attempt was made to generate the identifiers automatically. However,
since mechanisms to achieve this always work inside only one compilation
unit, there is no way to preserve the same identifiers for the same entities
throughout different compilation units. Hence, inevitably, we run into the
ODR violation. An attempt to work this around by defining stuff in
anonimous namespace, forces everything that uses this facility to also be
defined in the anonimous namespace or, again, the ODR is violated.
Although compilers don't seem to care much about ODR in this particular case
(meta-functions only, no runtime constructs), somehow I feel uneasy to
proceed with the solution that does not quite comply to the standard. And
so, once again, I tried to come-up with a registration scheme that, on the
one hand, would not violate the ODR, and on the other hand, would not put a
significant burden on the end user -- the crutial condition for this library
to be of any use at all.
Here is this scheme:
The typeof library registers primitive types, as well as some other standard
stuff, like functions, pointers, references, arrays, consts, etc. If the
user only wants any combination of those, no additional actions is required,
such as the following type:
int& (*)(char, double&)
would be handled.
If, in addition to this, the user wants to handle types from other
libraries, such as Spirit, Lambda, etc., it works as following:
- The typeof library #defines a preprocessor constant, currently called
BOOST_TYPEOF_USER_GROUP, which is the next identifier after the last one it
- The Spirit library registers its types against some symbolic identifiers.
Only one per file is required (inside the same file __LINE__ does the job).
Something looking similarly to the include guard seems like a decent choice.
In addition, Spirit defines a separate header where it enumerates all these
symbolic identifiers. This header looks something like this:
SPIRIT_REGISTER_HPP = BOOST_TYPEOF_USER_GROUP,
#define BOOST_TYPEOF_USER_GROUP SPIRIT_LAST
IOW, the Spirit identifiers start where the typeof library identifiers
ended, and BOOST_TYPEOF_USER_GROUP is, again, the next available.
- The Lambda library does the same, etc.
- The user combines all the groups in a single include, effectively chaining
- The user includes this file before any registration file provided by these
libraries. Since the file contains only enums, the compile-time penalty is
minimal. This insures that identifiers remain fixed across compilation
- Whenever the user needs to register her own classes, she provides
identifiers by any means she prefers, such as enum, etc., starting from
See http://groups.yahoo.com/group/boost/files/typeof.zip (or Spirit
repository, TYPEOF_DEV branch) for how it all works together.
I appologize for such a long post (if you are still here :) )
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk