c++ - compile-time counter for template classes -
imagine have lot of classes lot of different template parameters. every class has method static void f(). want collect these function pointers in list l.
a run-time solution easy:
typedef void (*p)(); std::vector<p> l; int reg (p x) { static int = 0; l.push_back(x); return i++; } // returns unique id template <typename t> struct regt { static int id; }; template <typename t> int regt<t>::id = reg (t::f); template < typename ... t > struct class1 : regt< class1<t...> > { static void f(); }; template < typename ... t > struct class2 : regt< class2<t...> > { static void f(); }; // etc. the compiler knows f()s of instantiated classes @ compile-time. so, theoretically should possible generate such list (a const std::array<p, s> l s) compile-time constant list. how? (c++0x solutions welcome, too).
why need this?
on architecture 256 kb (for code , data), need generate objects incoming ids of classes. existing serialization frameworks or run-time solution above unnecessarily big. without templates compile-time solution easy, want keep advantages templates offer.
manually
the simplest thing can roll code manually, don't think there can used advantage templates, use plain classes, a, b... stand particular instantiations of types. allows compile time initialization of types, @ cost of having remember update lookup table whenever new type added system:
typedef void (*function_t)(); function_t func[] = { &a::f, &b::f, &c::f }; i recommend this, maintenance point of view. automating system make code harder understand , maintain in future.
macros
the simple automated one, generate less code macro generation system using macros. since first approach use extensive use of macros, generate functions automatically, did in previous question. can remove part of code if have (hopefully) given path of full code generation through macros.
to avoid having retype names of types in different contexts can define macro data need context, , use other macros filter used (and how) in each particular context:
// actual list of types, id , code // generating in other question static function: #define foreach_type( macro ) \ macro( a, 0, { std::cout << "a"; } ) \ macro( b, 1, { std::cout << "b"; } ) \ macro( c, 2, { std::cout << "c"; } ) // use recursive macro to: // create enum , calculate number of types used #define enum_item( type, id, code ) \ e_##type, enum alltypes { foreach_type( enum_item ) alltypes_count }; #undef enum_item // can create array of function pointers typedef void (*function_t)(); function_t func[ alltypes_count ]; // can create classes: #define create_type( type, the_id, code ) \ struct type {\ static const int id = the_id; \ static void func() code\ }; foreach_type( create_type ) #undef create_type // , create function #define register_type( type, id, code ) \ func[ i++ ] = &type::func; void perform_registration() { int = 0; foreach_type( register_type ); }; #undef register_type // , can test int main() { perform_registration(); ( int = 0; < alltypes_count; ++i ) { func[ ](); } } this is, on other hand maintenance nightmare, quite fragile , hard debug. adding new types trivial, add new line foreach_type macro , done... , best of lucks once fails...
templates , metaprogramming
on other hand, using templates can close cannot single point of definition types. can automate of operations in different ways, @ least need define types , add them typelist rest of functionality.
simplifying definition of actual type_list c++0x code can start defining types , creating type_list. if want avoid using c++0x, take @ loki library, c++0x type list simple enough:
template <typename ... args> type_list {}; // generic type list typedef type_list< a, b, c, d > types; // our concrete list of types a, b, c , d // source of duplication: // types must defined , added // type_list manually [*] now need use metaprogramming operate on type list, can example count number of elements in list:
template <typename list> struct size; // declare template <typename t, typename ... args> // general case (recursion) struct size< type_list<t,args...> > { static const int value = 1 + size< type_list<args...>::value; }; template <> // stop condition recursion struct size< type_list<> > { static const int value = 0; }; having size of type list first step in our problem, allows define array of functions:
typedef void (*function_t)(); // signature of each function pointer struct registry { static const int size = ::size< types >::value; static const function_t table[ size ]; }; function_t registry::table[ registry::size ]; // define array of pointers now want register static functions each particular type in array, , create auxiliar function (encapsulated static function in type allow partial specializations). note concrete part designed run during initialization: not compile time, cost should trivial (i more worried on binary size templates):
template <typename t, int n> // declaration struct register_types_impl; template <typename t, typename ... args, int n> // general recursion case struct register_types_impl< type_list<t,args...>, n> { static int apply() { registry::table[ n ] = &t::f; // register function pointer return register_types_impl< type_list<args...>, n+1 >; } }; template <int n> // stop condition struct register_types_impl< type_list<>, int n> { static int apply() { return n; } }; // , nicer interface: int register_types() { register_types_impl< types, 0 >(); } now need id function maps our types function pointer, in our case position of type in type list
template <typename t, typename list, int n> // same old, same old... declaration struct id_impl; template <typename t, typename u, typename ... args, int n> struct id_impl< t, type_list<u, args...>, n > { // general recursion static const int value = id_impl< t, type_list<args...>, n+1 >; }; template <typename t, typename ... args, int n> // stop condition 1: type found struct id_impl< t, type_list<t, args...>, n> { static const int value = n; }; template <typename t, int n> // stop condition 2: type not found struct id_impl< t, type_list<>, n> { static const int value = -1; } // , cleaner interface template <typename t, typename list> struct id { static const int value = id_impl<t, list, 0>::value; }; now need trigger registration @ runtime, before other code:
int main() { register_types(); // build lookup table } [*] well... sort of, can use macro trick reuse types, use of macros limited, not hard maintain/debug.
Comments
Post a Comment