Background
C++11 initializer lists can be used to initialize vectors and arrays with argument passing to constructors.
I have a piece of code below where I would like to initialize such an array with all the enumerations of eCOLORS from eCOLORS::First to eCOLORS::Last using initializer lists.
Original Code
Since all information is known at compile time, I think there is a way to solve this problem.
enum class eCOLORS
{
kBLUE=0, kGREEN, kRED, kPURPLE,
First=kBLUE, Last=kPURPLE
};
template< typename E >
size_t Size()
{
return (size_t)(E::Last) - (size_t)(E::First) + 1;
}
struct Foo
{
Foo( eCOLORS color ) { }
};
int main(int argc, char** argv)
{
Foo a[2] = {
{ eCOLORS::kRED },
{ eCOLORS::kGREEN }
}; // works, but requires manual maintenance if I add another color
/* how to feed v with all the enums from First to Last
without hard-coding?
Foo v[Size<eCOLORS>()] = {
};
*/
}
Ugly Pseudo-Answer
The consensus appears to be that there is currently no way to this.
My original intent in asking this question is, I want to automagically
create an array of Foo objects whose initialization is solely based on
the enumeration of eColors. I wanted a no maintenance solution that
would work even after you add more entries into eColors.
Using the Enum class from this earlier post, I can write a function template that gives me the functionality that I need. Even without using that Enum class, you could still loop from eCOLORS::First to eCOLORS::Last, along with some ugly casts.
My ugly pseudo-answer is kludgy (nowhere as nice as a compile-time initializer list), but at least it is zero maintenance.
NOTE: if better solutions come up, I will update the OP accordingly.
template <typename T, typename E>
std::vector< T >
Initialize_With_Enums()
{
std::vector< T > v;
for( auto p : Enum<E>() )
v.push_back( T( p ));
return v;
}
int main( int argc, char** argv )
{
auto w = Initialize_With_Enum<Foo,eCOLORS>();
}
You can do this with variadic templates and what I'm going to call the "indices trick".
typedef std::underlying_type<eCOLORS>::type underlying;
// just a type to carry around variadic pack of numbers
template <underlying...> struct indices {};
// A template to build up a pack of Count numbers from first
// third parameter is an accumulator
template <underlying First, underlying Count, typename Acc = indices<>>
struct make_indices;
// base case
template <underlying X, underlying... Acc>
struct make_indices<X, 0, indices<Acc...>> { typedef indices<Acc...> type; };
// recursive build up of the pack
template <underlying First, underlying Count, underlying... Acc>
struct make_indices<First, Count, indices<Acc...>>
: make_indices<First, Count-1, indices<First+Count-1, Acc...>> {};
size_t const max_colors = underlying(eCOLORS::Last) - underlying(eCOLORS::First)+1;
// shortcut
typedef make_indices<
underlying(eCOLORS::First),
max_colors
>::type all_eCOLORS_indices;
// takes a dummy parameter with the pack we built
template <underlying... Indices>
std::array<eCOLORS, max_colors> const& all_colors(indices<Indices...>) {
// convert each number to the enum and stick it in an static array
static std::array<eCOLORS, max_colors> const all = {
eCOLORS(Indices)...
};
return all;
}
std::array<eCOLORS, max_colors> const& all_colors() {
// create a dummy object of the indices pack type and pass it
return all_colors(all_eCOLORS_indices());
}
This assumes all the enumerators are sequential, and needs std::underlying_type which is not supported in GCC 4.6 (will be in 4.7, but you can emulate it to a certain extent).
I don't think you can do this with initializer lists. This isn't the sort of thing they're meant for. I think you could manage a decent workaround by defining an iterator that would iterate over any enumeration that had a First and Last member.
But first, your definition of Size isn't quite right...
template< typename E >
constexpr size_t Size()
{
return (size_t)(E::Last) - (size_t)(E::First) + 1;
}
Declaring it constexpr means that it's definition is a compile time constant. So you can use it in template arguments and the like.
I don't have time right now to create the range class for you. It's somewhat complicated by the fact that enum values and integers are not interchangeable for enum classes. But it's not too hard. You might use this question "Is there a range class in C++0x (aka C++11) for use with range based for loops?" as a starting point. You basically use the vector initializer that initializes from a [begin, end) pair in conjuction with a range class like is discussed in that question.
There isn't an automatic way to do this using initializer lists, but you could do it algorithmically if you know the first and last values of the enum by just using a for loop to insert the values.
The MACRO solution.
#include <stdio.h>
#include <initializer_list>
#define COLORS(V,E) \
V(RED) \
V(GREEN) \
E(BLUE)
#define COMMA(V) \
V,
#define NCOMMA(V) \
V
#define SCOMMA(V) \
#V,
#define SNCOMMA(E) \
#E
enum Colors {
COLORS(COMMA,NCOMMA)
};
const char * colors[] = {
COLORS(SCOMMA,SNCOMMA)
};
#define INIT_LIST(V) \
{ V(COMMA,NCOMMA) }
int main(int argc, char **argv) {
for ( auto i : INIT_LIST(COLORS) ) {
printf("%s\n", colors[i]);
}
}
I like your problem. Long ago, this kind of thing used to be handled with X macros http://www.drdobbs.com/the-new-c-x-macros/184401387
I'm a c++11 newb, but after some fiddling around I've got some kind of solution (g++ 4.8.4):
enum class Symbols { FOO, BAR, BAZ, First=FOO, Last=BAZ };
I kept your Size() but added some other boilerplate to make the initialization, lower down, easier to read.
template< typename E > constexpr size_t Size() { return (size_t)(E::Last) - (size_t)(E::First) + 1; }
template< typename E > constexpr size_t as_sizet( E s ) { return (size_t)s; }
template< typename E > constexpr E operator++( E& s, int ) { return (E)(1 + (size_t)s); }
template< typename E > constexpr bool operator<=( E& a, E& b ) { return (size_t)a < (size_t)b; }
There are two bits of magic here:
We return a reference to the initialized array (itself another template parameter)
We initialize a static array during the recursive call using a junk argument
Like so:
template< typename E, typename EARR >
constexpr EARR& init_array( EARR& zArr, E sym = E::First, E junk = E::Last )
{
return sym <= E::Last ? init_array( zArr, sym++, zArr[ as_sizet( sym ) ] = sym ) : zArr;
}
In the end, it comes together with:
The typedef
The static declaration of the array
The reference to the array that gets initialized
Like so:
typedef Symbols SymbolArr[ Size<Symbols>() ];
static SymbolArr symbolArr;
SymbolArr& symbolArrRef = init_array<Symbols, SymbolArr>(symbolArr);
Edit:
The junk parameter in the recursive initialization function can be removed using:
template< typename E > constexpr E next( E& s ) { return (E)(1 + (size_t)s); }
template< typename E, typename EARR >
constexpr EARR& init_array( EARR& zArr, E sym = E::First )
{
return sym <= E::Last ? init_array( zArr, next( zArr[ as_sizet( sym ) ] = sym ) ) : zArr;
}
Related
Suppose we have some sort of templated struct:
template<typename T>
struct S {};
We want to create an std::array of some object of type S. But, because S is a template struct, we need to create a base class for keeping those objects.
struct AbstractS {};
template<typename T>
struct S : AbstractS {};
Now, suppose that we have a constexpr function that creates an std::array and returns it and a constexpr function for get that array.
constexpr auto createArray() {
constexpr auto v1 = S<int>{};
constexpr auto v2 = S<double>{};
std::array<const AbstractS *, 2> values{ &v1, &v2 };
return values;
}
constexpr void getArray() {
constexpr auto values = createArray();
}
This code doesn't compile and I think this is because the addresses of v1 and v2 are not constants.
Let me give you a concrete example of what I'm trying to do.
struct AbstractPolynomial {};
template<typename T, std::size_t Degree>
struct Polynomial : AbstractPolynomial {};
I have a struct that models a polynomial function, where T is type of values of coefficients of polynomial and Degree is the polynomial Degree.
template<std::size_t N>
constexpr auto createArray() {
std::array<AbstractPolynomial *, N> polynomials;
for (std::size_t i = 0; i < N; i++) {
if (i % 2 == 0) {
polynomials[i] = &Polynomials<T1>{5};
} else {
polynomials[i] = &Polynomials<T2>{2, 5};
}
}
return polynomials;
}
Suppose that Polynomial has a deduction guide (I didn't implement it here). I know that you cannot get the address of a temporary and assigning lines are incorrect, but I put in this form because I want you to give me a solution for this scenario.
Can we do some sort of tricks to create a scenario like this?
This is the solution, note that the Polynomials need to be generated initially preserving their compile-time type.
The later choice to not use that information for whatever reason (dynamic access) is yours.
Note the data structure is constexpr, but not necessarily the evaluation of the polynomial.
I think this is because virtual interferes with the constexpr-ness capabilities of the evaluate function.
You can play with the solution here: https://godbolt.org/z/oMreTzoGP
#include<array>
#include<cassert>
#include<tuple>
#include<utility>
// Dynamic polymials (questionable use of virtual functions)
struct AbstractPolynomial {
virtual auto evaluate() const -> double = 0;
};
template<typename T>
struct Polynomial : AbstractPolynomial {
constexpr Polynomial(T t) : value_{t}{}
T value_;
auto evaluate() const -> double override;
};
// instantiate and define two child classes for illustration
template<> auto Polynomial<double >::evaluate() const -> double {return value_;}
template<> auto Polynomial<std::pair<double, double>>::evaluate() const -> double {return value_.first + value_.second;}
// Metaprogramming in this block doesn't assume virtual functions, Polynomial can be a concrete class
// functional form (on index and constructor args) taken from OP example
constexpr auto makePoly(std::integral_constant<int, 0>){return Polynomial<double >{5.};}
constexpr auto makePoly(std::integral_constant<int, 1>){return Polynomial<std::pair<double, double>>({2., 5.});}
// Tuples (not arrays) are created here
template <std::size_t... I>
constexpr auto createTuple_aux(std::index_sequence<I...>){
// do different things for even/odd cases (again taken from OP example)
return std::make_tuple(makePoly(std::integral_constant<int, I % 2>{})...);
}
template <std::size_t N> constexpr auto createTuple(){return createTuple_aux(std::make_index_sequence<N>{});}
// create 10 non-polymorphic polynamials in a tuple (preserve type information)
constexpr auto polyTuple = createTuple<10>();
// create 10 polymorphic polynamials in an array via pointers (type information is kept in the virtual table in pointer elements)
constexpr auto polyArrayPtr = std::apply([](auto const&... e){return std::array<AbstractPolynomial const*, std::tuple_size<decltype(polyTuple)>{}>{&e...};}, polyTuple);
int main(){
// test non-polymorphic access
assert( std::get<0>(polyTuple).evaluate() == 5. );
assert( std::get<1>(polyTuple).evaluate() == 7. );
assert( std::get<2>(polyTuple).evaluate() == 5. );
// test polymorphic access, indiraction
constexpr auto check = polyArrayPtr.size();
assert( polyArrayPtr[0]->evaluate() == 5. );
assert( polyArrayPtr[1]->evaluate() == 7. );
assert( polyArrayPtr[2]->evaluate() == 5. );
}
I am not an expert in constexpr. But intuition tells me your elements must be global and, for example, static to the function.
Since this is not possible in a constexpr function (the compiler tells me) I put them outside and the whole thing works.
#include<array>
struct AbstractS {};
template<typename T>
struct S : AbstractS {};
namespace detail{
constexpr auto v1 = S<int>{};
constexpr auto v2 = S<double>{};
}
constexpr auto createArray() {
constexpr std::array<const AbstractS *, 2> values{ &detail::v1, &detail::v2 };
return values;
}
constexpr void getArray() {
constexpr auto values = createArray();
}
int main(){}
https://godbolt.org/z/1q6MbMhdM
My ultimate goal is to achieve a mapping from a set of two character strings to corresponding data types:
"AA" --> char
"BB" --> int
"CC" --> float
"DD" --> std::complex<double>
and so on ...
The best "not quite working" solution I can come up with is two parts. The first part is using std::map to map between the strings and corresponding enum values. The second part uses a templated type alias and std::conditional to map the enum values to types.
enum class StrEnum { AA, BB, CC, DD };
// STEP 1: string --> enum
// !! std::map will not work here since a constexpr is required !!
std::map<std::string, StrEnum> str_map = {
{"AA", StrEnum::AA},
{"BB", StrEnum::BB},
{"CC", StrEnum::CC},
{"DD", StrEnum::DD}
};
// STEP 2: enum --> type
template<StrEnum val> using StrType = typename std::conditional<
val == StrEnum::AA,
char,
typename std::conditional<
val == StrEnum::BB,
int,
typename std::conditional<
val == StrEnum::CC,
float,
std::complex<double>
>::type
>::type
>::type;
Goal Usage:
StrType<str_map["BB"]> myVal; // <-- does not work in current state with std::map
As more value mappings are added the above nesting can get very nasty.
Is there a better/cleaner/working overall way to achieve this mapping? I'm especially interested in STEP 2 and whether there is a way to have less nesting.
I am using C++11. (but if the only answer lies in C++14 or beyond it would be nice to at least be aware of it)
Since std::map does not have constexpr ctors, your template argument str_map["BB"] can not be evaluated at compile-time.
A simple and maintainable way to map integers to types would be using std::tuple and std::tuple_element as follows.
For instance, StrType<0> is char, StrType<1> is int, and so on:
using types = std::tuple<char, int, float, std::complex<double>>;
template<std::size_t N>
using StrType = typename std::tuple_element<N, types>::type;
Then the problem is how to map strings to integers in C++11.
First, strings can be compared at compile-time by the accepted answer in this post.
Second, we can use the ternary operator in compile-time evaluations.
Thus, at least the following function getIdx can map each string to the corresponding integer at compile-time.
For instance, getIdx("AA") is zero:
constexpr bool strings_equal(const char* a, const char* b) {
return *a == *b && (*a == '\0' || strings_equal(a + 1, b + 1));
}
constexpr std::size_t getIdx(const char* name)
{
return strings_equal(name, "AA") ? 0:
strings_equal(name, "BB") ? 1:
strings_equal(name, "CC") ? 2:
strings_equal(name, "DD") ? 3:
4; // compilation error
}
You can use these functions for the current purpose as follows:
DEMO
StrType<getIdx("BB")> x; // x is int.
constexpr const char* float_type = "CC";
StrType<getIdx(float_type)> y; // y is float.
static_assert(std::is_same<StrType<getIdx("AA")>, char> ::value, "oops."); // OK.
static_assert(std::is_same<StrType<getIdx("BB")>, int> ::value, "oops."); // OK.
static_assert(std::is_same<StrType<getIdx("CC")>, float>::value, "oops."); // OK.
static_assert(std::is_same<StrType<getIdx("DD")>, std::complex<double>>::value, "oops."); // OK.
I recentlly worked on something like that. My proposed solution was something like this (with a lot more stuff but here the main idea):
//Define a Type for ID
using TypeIdentifier = size_t;
//Define a ID generator
struct id_generator {
static TypeIdentifier create_id() {
static TypeIdentifier value = 0;
return value++;
}
};
//Define some kind of handler for place
struct type_id_handler {
static std::vector<std::function<void(void*)>> type_handler;
};
std::vector<std::function<void(void*)>> type_id_handler::type_handler;
//Define id's and make a basic functions
template<typename T>
struct type_id_define {
static TypeIdentifier get_id() {
static TypeIdentifier id = id_generator::create_id();
static auto one_time_stuff = [] () -> bool {
type_id_handler::type_handler.resize(id+1);
type_id_handler::type_handler[id] = [](void* ptr) {
auto * object = static_cast<T*>(ptr);
//do stuff your type
std::cout << __PRETTY_FUNCTION__ << std::endl;
};
return true;
}();
return id;
}
};
For main dummy test:
int main() {
std::map<std::string, TypeIdentifier> typeMap {
{"AA", type_id_define<char>::get_id()},
{"BB", type_id_define<int>::get_id()},
};
int a;
char b;
type_id_handler::type_handler[typeMap["BB"]](&a);
type_id_handler::type_handler[typeMap["AA"]](&b);
return 0;
}
Output should be:
type_id_define<T>::get_id()::<lambda()>::<lambda(void*)> [with T = int]
type_id_define<T>::get_id()::<lambda()>::<lambda(void*)> [with T = char]
The main idea is to make a new type_id_define with the proper id for each type and use it as a index for select the correct function to execute. Also when generating the id it store a function for cast from void* to given type (I use void* for store different type function on same vector) After that you can use std::any, void*, or whatever you want for pass the object to the function and get type-safety.
If you want to use something like that I Also recommend you to think about better way for register types and add the corresponding function.
Is it possible to constrain the type of arguments in a variadic constructor?
I want to be able to express
X x1(1,3,4);
X x2(3,4,5);
// syntax error: identifier 'Args'
class X {
template<int ... Args> X(Args...)
{
}
};
// this works but allows other types than int
class Y {
template<typename ... Args> Y(Args...)
{
}
};
edit to clarify intent:
What I want to achieve is to store data passed into a constructor (constants known at compile time) into a static array.
so there are some other
template<int ...values>
struct Z
{
static int data[sizeof...(values)];
};
template<int ... values>
int Z<values...>::data[sizeof...(values)] = {values...};
and in the constructor of X I would like to use Z like this:
class X {
template<int ... Args> X(Args...)
{
Z<Args...>::data // do stuff with data
}
};
Is that possible, our do I have to use integer_sequence?
You can use std::initializer_list:
#include <iostream>
#include <initializer_list>
void myFunc(std::initializer_list<int> args)
{
for (int i: args) std::cout << i << '\n';
}
int main(){
myFunc({2,3,2});
// myFunc({2,"aaa",2}); error!
}
Since you have the following:
template<int... values>
struct Z
{
static int data[ sizeof...( values ) ];
};
template <int... values>
int Z<values...>::data[ sizeof...( values ) ] = { values... };
You can use std::integer_sequence<> to pass in the ints to Z<>:
struct X
{
template <int... values>
X( std::integer_sequence<int, values...> )
{
for ( int i{ 0 }; i < sizeof...( values ); ++i )
Z<values...>::data[ i ]; // do stuff with data
}
};
You can make yourself a helper type to make it easy to call the ctor:
template <int... values>
using int_sequence = std::integer_sequence<int, values...>;
Then you can instantiate your class like so:
int main()
{
X x( int_sequence<1, 3, 5>{} );
}
You've updated your question to indicate that all you need is a compile-time std::integer_sequence, which is great.
But just for the sake of future readers who might come here looking for the answer, I'd like to also answer your original question "Is it possible to constrain the type of arguments in a variadic constructor?"
Yes. One way (the best way? I'm not sure) is to SFINAE on an extra template parameter, like this:
struct X {
template<
class... TT,
class E = std::enable_if_t<(std::is_same_v<TT, int> && ...)>
>
X(TT... tt) {
// do stuff with the ints "tt..."
}
};
The && ... is a fold-expression, new in C++17. If your compiler doesn't support fold-expressions, just replace that with a hand-rolled all_of.
No, you can't constrain the type. You can use static_assert though. Would be something like this:
static_assert(std::is_same<int, Args>::value ..., "have to be ints.");
Have not tried to use an expansion in a static_assert like that though. You may need a constexpr that returns bool or something.
I have some code that uses two different type of colours, 8 bit per channel and 16 bit per channel, each represented by a struct. In order to effectively reuse my code I have a template function that does some rendering with them. I would therefore like a templated function to grab the max value of a channel of my colours.
My initial attempt looked like this. I have only shown the specialization for 8 bpc
struct Pixel8
{
unsigned char r;
unsigned char g;
unsigned char b;
};
#define PIXEL8_MAX 255
template <class PIXEL>
auto getMax( ) -> decltype( PIXEL::r )
{
static_assert( sizeof(PIXEL) > 0, "getMax can only be called with a pixel type." );
}
template <>
auto getMax<Pixel8>( ) -> decltype( Pixel8::r )
{
return PIXEL8_MAX;
}
This would not compile with Visual studio 2012. I get the error
1> error C2785: ''unknown-type' getMax(void)' and 'char getMax(void)' have different return types
1> see declaration of 'getMax'
To me I feel that this should work but I have been unable to find any examples. There is one other question similar at Specialize function template with decltype trailing return type, but here the return type is the same for each specialization.
I have found a workaround which I will post as an answer so that others can benefit. However it is not very transparent so if someone can tell me if the code above is valid and this is a VC++ incompatibility or if it is not valid then why and how I can make it valid?
Try to make the return type depend on the template parameter type:
struct Pixel8
{
char r;
char g;
char b;
};
template<typename T>
struct ColourType
{
typedef decltype(T::r) type;
};
#define PIXEL8_MAX 255
template <class PIXEL>
typename ColourType<PIXEL>::type getMax()
{
static_assert(false, "getMax can only be called with a pixel type.");
}
template <>
ColourType<Pixel8>::type getMax<Pixel8>()
{
return PIXEL8_MAX;
}
This is a workaround for getting the required behaviour. It relies on the fact that there is another way to define a type in C++ without using decltype. This is to use typname.
It's main use in this context is as follows, imagine two classes and two functions
class MyClass
{
public:
class MyThing
{
};
};
class MyOtherClass
{
public:
static int MyThing;
}
template< class T >
void func1( T something )
{
typename T::MyThing thing;
}
template< class T >
void func2( T something )
{
T::MyThing = 5;
}
If we pass either class as a template parameter T, then T::MyThing would be a type for MyClass and a static int for MyOtherClass. These are entirely incompatible, so we use typename to separate them.
In func1 we use typename to state that T::MyThing is a type. We could pass in a MyClass object. In func2 we omit typename and therefore T::MyThing is interpreted as a variable and we could pass in a MyOtherClass. Without typename, there would be no way to tell if T::MyThing was a type or a static variable.
Note also that typename can refer to a typdef as well as an internal class, So if we create a templated class or struct which includes a typedef for a type we can access that type using typename.
template<class PIXEL>
struct pixTypes
{
};
template<>
struct pixTypes<Pixel8>
{
typedef char type;
};
template <class PIXEL>
auto getMax( ) -> typename pixTypes<PIXEL>::type
{
static_assert( false, "getMax can only be called with a pixel type." );
}
template <>
auto getMax<Pixel8>() -> typename pixTypes<Pixel8>::type
{
return PIXEL8_MAX;
}
So now we get our return type from a typename which refers to a typedef in a specialized templated struct.
This seems rather a convoluted way around everything, but it does compile on Visual Studio 12.
Using macros to define constants, as in this question's code
#define PIXEL8_MAX 255
… is not ideal.
Also, the definition conflicts with the type used. A char is not guaranteed to have that maximum value, and with most implementations will by default not have that maximum value. You can define a Byte type as unsigned char, but even so you're not guaranteed 8 bits, and should check that.
The standard library provides the numeric_limits class template to deal with maximum values etc.:
#include <limits> // std::numeric_limits
#define STATIC_ASSERT( e ) static_assert( e, #e )
using Byte = unsigned char;
int const bits_per_byte = std::numeric_limits<Byte>::digits;
STATIC_ASSERT( bits_per_byte == 8 );
struct Pixel8
{
Byte r;
Byte g;
Byte b;
};
template< class Pixel >
constexpr auto getMax() -> decltype( Pixel::r )
{
return std::numeric_limits<decltype( Pixel::r )>::max();
}
#include <iostream>
using namespace std;
auto main() -> int
{
cout << +getMax<Pixel8>() << endl;
}
Imagine that you have a lot of classes with a lot of different template parameters. Every class has a method static void f(). You want to collect all these function pointers in a list L.
A run-time solution would be easy:
typedef void (*p)();
std::vector<p> L;
int reg (p x) { static int i = 0; L.push_back(x); return i++; } // also returns an 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 all f()s of all instantiated classes at compile-time. So, theoretically it should be possible to generate such a list (a const std::array<p, S> L with some S) as a compile-time constant list. But how? (C++0x solutions are welcome, too).
Why do I need this?
On an architecture with only 256 kB (for code and data), I need to generate objects for incoming ids of classes. Existing serialization frameworks or the run-time solution above are unnecessarily big. Without templates a compile-time solution would be easy, but I want to keep all the advantages templates offer.
Manually
The simplest thing that you can do is just roll the code manually, I don't think that there is much that can be used to your advantage from the templates, so I will use plain classes, where A, B... stand for particular instantiations of your types. That allows for compile time initialization of the types, at the cost of having to remember to update the lookup table whenever a new type is added to the system:
typedef void (*function_t)();
function_t func[] = {
&A::f,
&B::f,
&C::f
};
I would recommend this, from a maintenance point of view. Automating the system will make the code much harder to understand and maintain in the future.
Macros
The simple most automated one, which will probably generate less code is a macro generation system is just using macros. Since this first approach will use extensive use of macros, I will generate the functions automatically, as you did in the previous question. You can remove that part of code if you have (hopefully) given up the path of full code generation through macros.
To avoid having to retype the names of the types in different contexts you can define a macro with all the data you need for any context, and then use other macros to filter what is to be used (and how) in each particular context:
// This is the actual list of all types, the id and the code that you were
// generating in the other question for the static function:
#define FOREACH_TYPE( macro ) \
macro( A, 0, { std::cout << "A"; } ) \
macro( B, 1, { std::cout << "B"; } ) \
macro( C, 2, { std::cout << "C"; } )
// Now we use that recursive macro to:
// Create an enum and calculate the number of types used
#define ENUM_ITEM( type, id, code ) \
e_##type,
enum AllTypes {
FOREACH_TYPE( ENUM_ITEM )
AllTypes_count
};
#undef ENUM_ITEM
// Now we can create an array of function pointers
typedef void (*function_t)();
function_t func[ AllTypes_count ];
// We can create all 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
// And create a function that will
#define REGISTER_TYPE( type, id, code ) \
func[ i++ ] = &type::func;
void perform_registration() {
int i = 0;
FOREACH_TYPE( REGISTER_TYPE );
};
#undef REGISTER_TYPE
// And now we can test it
int main() {
perform_registration();
for ( int i = 0; i < AllTypes_count; ++i ) {
func[ i ]();
}
}
This is, on the other hand a maintenance nightmare, quite fragile and hard to debug. Adding new types is trivial, just add a new line to the FOREACH_TYPE macro and you are done... and the best of lucks once something fails...
Templates and metaprogramming
On the other hand, using templates you can get close but you cannot get to the single point of definition for the types. You can automate some of the operations in different ways, but at the very least you will need to define the types themselves and add them to a typelist to get the rest of the functionality.
Simplifying the definition of the actual type_list with C++0x code you can start by defining the types and then creating the type_list. If you want to avoid using C++0x, then take a look at the Loki library, but with C++0x a type list is 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 and D
// this is the only source of duplication:
// types must be defined and added to the
// type_list manually [*]
Now we need to use some metaprogramming to operate on the type list, we can for example count the number of elements in the 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 for the recursion
struct size< type_list<> > {
static const int value = 0;
};
Having the size of the type list is a first step in our problem, as it allows us to define an 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 the array of pointers
Now we want to register the static functions from each particular type in that array, and for that we create an auxiliar function (encapsulated as a static function in a type to allow for partial specializations). Note that this concrete part is designed to be run during initialization: it will NOT be compile time, but the cost should be trivial (I would be more worried on the binary size with all the 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; }
};
// and a nicer interface:
int register_types() {
register_types_impl< types, 0 >();
}
Now we need an id function that maps our types to the function pointer, which in our case is the position of the type in the 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;
}
// and a cleaner interface
template <typename T, typename List>
struct id {
static const int value = id_impl<T, List, 0>::value;
};
Now you just need to trigger the registration at runtime, before any other code:
int main() {
register_types(); // this will build the lookup table
}
[*] Well... sort of, you can use a macro trick to reuse the types, as the use of macros is limited, it will not be that hard to maintain/debug.
The compiler knows all f()s of all instantiated classes at compile-time.
There's your mistake. The compiler knows nothing about template instantiations in other compilation units. It should now be pretty obvious why the number of instantiations isn't a constant integral expression that could be used as a template argument (and what if std::array was specialized? Halting Problem ahead!)