|
Boost : |
From: Thomas Wenisch (twenisch_at_[hidden])
Date: 2003-08-05 14:06:01
Hello all,
Recently, I had the following problem in some library code I maintain.
I provide users with a macro which generates fairly complex typedefs:
#define MAKE_TYPEDEF(Ident, etc) typedef complicated Identifier;
Users use the macro to define a bunch of different types, all with
identifiers of the user's choosing. Elsewhere, I need an mpl::list of the
types generated by the macro. Right now, users have to supply me with
this mpl::list. This opens the possibility for the list to get out of
sync with the MAKE_TYPEDEF()s if the user forgets to update the list when
adding a new MAKE_TYPEDEF() invocation.
I wanted to eliminate this possible source of bugs, by somehow
automatically generaring the mpl::list as a side effect of using the
MAKE_TYPEDEF macro. I want each macro invocation of MAKE_TYPEDEF to
mpl::push_front<> the type it declares onto the front of a typelist.
#define MAKE_TYPEDEF() ... \
typedef mpl::push_front< previous_list, Ident>::type new_list;
The problem is the naming of the identifiers previous_list and
new_list. Because of ODR, each invocation of MAKE_TYPEDEF needs to give a
unique identifier to new_list, and yet somehow figure out what name was
used for previous_list. The easy solution is to have the user provide
unique names and chain the list of MAKE_TYPEDEFs together. But, this is
just as error prone as having the user simply provide the mpl::list
directly.
I wanted a solution which is completely transparent to the user. Thus, I
cooked up the following, which I am calling "mutable typedef".
The solution abuses the __LINE__ macro to generate unique identifier
names. A template specialization is created to mark the line number
used in the identifier. Then, when I wish to determine the name of
the identifier, a second template tests each line number going
upward from the current line until it finds the line number marked by the
specialization. Using this trick, I can create the effect of a
typedef which can be redefined, without violating ODR. Each use of the
typedef finds the nearest preceeding definition.
The code demostrating the trick appears below. The same trick can be used
to redefine integral constants as well, but I omit that code for brevity.
The main disadvantages of the approach are that it is not extremely robust
(doesn't work if line numbers change, i.e. when including files; or if
multiple mutable typedefs appear on one line), and that it requires a
large number of template instantiations if the nearest definition of the
mutable typedef is far away.
My questions for the Boost community are:
1) Is there an easier way to accomplish what is outlined in my use case
above, using preprocessor tricks or something similar?
2) Is there a way to enhance the mutable typedef trick to avoid the
large number of template instantiations?
3) Are there others interested in using this or seeing it submitted to
Boost?
Regards,
-Tom Wenisch
Computer Architecture Lab
Carnegie Mellon University
=== Example code begines here. Tested with gcc 3.3 ===
#include <iostream>
#include <boost/preprocessor/cat.hpp>
//Create the base templates for a line marker for Identifier and declare
//the struct used to search for the nearest previous occurence of the
//marker
#define DECLARE_LAST_LINE_MARKER(Identifier) \
template <int N> \
struct BOOST_PP_CAT(last_line_marker_,Identifier) \
: public BOOST_PP_CAT(last_line_marker_,Identifier)<N-1> {}; \
template <int StartAtLine> \
struct BOOST_PP_CAT(find_last_line_,Identifier) \
: public BOOST_PP_CAT(last_line_marker_,Identifier)< StartAtLine > {} /**/
//Mark the current line
#define LAST_LINE_MARKER(Identifier) \
template<> struct BOOST_PP_CAT(last_line_marker_,Identifier)<__LINE__> \
{ static const int value = __LINE__; } /**/
//Find the line number where the mark was last set
#define FIND_LAST_LINE_MARKER(Identifier) \
BOOST_PP_CAT(find_last_line_,Identifier)<__LINE__ - 1>::value /**/
//Declare the base template for a mutable typedef.
#define DECLARE_MUTABLE_TYPEDEF(Identifier) \
template< int UniqueId > \
struct BOOST_PP_CAT(mutable_typedef_,Identifier); \
DECLARE_LAST_LINE_MARKER(Identifier) /**/
//Set the type of mutable typedef Identifier to Type.
#define SET_MUTABLE_TYPEDEF(Identifier, Type) \
template<> \
struct BOOST_PP_CAT(mutable_typedef_,Identifier)<__LINE__> \
{ typedef Type type; }; \
LAST_LINE_MARKER(Identifier) /**/
//Returns the type of the mutable typedef. Can be used anywhere a type-id is legal.
#define GET_MUTABLE_TYPEDEF(Identifier) \
BOOST_PP_CAT(mutable_typedef_,Identifier)< FIND_LAST_LINE_MARKER(Identifier) >::type /**/
//Test code
DECLARE_MUTABLE_TYPEDEF( foo );
SET_MUTABLE_TYPEDEF( foo, char );
GET_MUTABLE_TYPEDEF( foo ) a;
SET_MUTABLE_TYPEDEF( foo, float );
GET_MUTABLE_TYPEDEF( foo ) b;
int main() {
std::cout << "typeof(a): " << typeid(a).name() << std::endl; //char
std::cout << "typeof(b): " << typeid(b).name() << std::endl; //float
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk