C++ metaprogramming - c++

I have the following problem:
Suppose I have some basic counter class Counter. And suppose we also have some sets of classes, that can be counted. Let's name some of them class CountedA and class CountedB.
Now, every class, which can be counted (such as CountedA and CountedB) has the following statically declared parts: one enum and one int part, that acts like a part of counted data.
For example, it's declaration could look the following way:
enum CountedType { A, B };
template <CountedType Type, int N>
class Counted { };
// Now we can declare 'CountedA' and 'CountedB'
typedef Counted<A, 25> CountedA;
typedef Counted<B, 7> CountedB;
Now, the declaration of the counter:
// C++0x variadic or simply bunch of 'typename XX' definitions for C++03
template <typename T0, typename T1, typename ...>
class Counter
{
// I don't know how to implement this
// for now!
int GetTotalN() { ... }
// Retrieve the corresponding type
// so that GetTypeAt<0> returns
// enum from 'T0'
template <int Pos>
CountedType GetTypeAt() { ... }
};
I want to be able to write something like:
class RealCounter : public Counter<CountedA, CountedB> { };
And use it the following way:
RealCounter counter;
int n = counter.GetTotalN();
CountedType type = counter.GetTypeAt<0>();
Now, I'm pretty sure that this can be done. But what's the best way of implementing it? (don't ask me why would I need such crazy kind of things :)
Does boost::mpl offer something for this case?
Thank you.
Small update:
In this particular example, GetTotalN() should return 25 + 7.
If we add, for example, typedef Counted<C, 2> CountedC, then the result for
RealCounter : public Counter<CountedA, CountedB, CountedC>
should become 25 + 7 + 2.

Here's C++03 code which works (for up to 10 template arguments). The main trick is giving class Counter a multiple inheritance, and passing objects of type Counter to function templates which must select a base class. The actual summation is done recursively.
Counter.hpp
enum CountedType { A, B };
template <CountedType Type, int N>
struct Counted {};
struct DummyCounted {};
template <int Pos, typename T>
struct IndexedType {};
template <unsigned int Terms>
struct PartialSum
{
template <typename CounterT>
static int getSum(const CounterT& ctr)
{ return PartialSum<Terms-1>::getSum(ctr) + ctr.template GetNAt<Terms>(); }
};
template <> struct PartialSum<0U>
{
template <typename CounterT>
static int getSum(const CounterT& ctr)
{ return ctr.template GetNAt<0>(); }
};
template <typename T0, typename T1=DummyCounted,
typename T2=DummyCounted, typename T3=DummyCounted,
typename T4=DummyCounted, typename T5=DummyCounted,
typename T6=DummyCounted, typename T7=DummyCounted,
typename T8=DummyCounted, typename T9=DummyCounted>
class Counter :
public IndexedType<0, T0>, public IndexedType<1, T1>,
public IndexedType<2, T2>, public IndexedType<3, T3>,
public IndexedType<4, T4>, public IndexedType<5, T5>,
public IndexedType<6, T6>, public IndexedType<7, T7>,
public IndexedType<8, T8>, public IndexedType<9, T9>
{
public:
static int GetTotalN() {
return PartialSum<9>().getSum( Counter() );
}
template <int Pos>
static CountedType GetTypeAt() { return _getTypeAt<Pos>( Counter() ); }
template <int Pos>
static int GetNAt() { return _getNAt<Pos>( Counter() ); }
private:
template <int Pos, CountedType Type, int N>
static CountedType _getTypeAt(const IndexedType<Pos, Counted<Type,N> >&)
{ return Type; }
template <int Pos, CountedType Type, int N>
static int _getNAt(const IndexedType<Pos, Counted<Type,N> >&)
{ return N; }
template <int Pos>
static int _getNAt(const IndexedType<Pos, DummyCounted>&)
{ return 0; }
};
Counter.cpp
#include "Counter.hpp"
#include <iostream>
typedef Counted<A, 25> CountedA;
typedef Counted<B, 7> CountedB;
class RealCounter : public Counter<CountedA, CountedB> {};
int main()
{
RealCounter counter;
int n = counter.GetTotalN();
CountedType type = counter.GetTypeAt<0>();
std::cout << "n is " << n
<< "\ntype check is " << (type == A) << std::endl;
return 0;
}
Output:
n is 32
type check is 1
That C++0x variadic template stuff looks interesting, but I haven't taken a good look at it yet. But I do think in C++0x, all this example's functions (except main of course) could be constexpr.

I'm not sure why you need to embed those parameters in the templates arguments and not simply in a constructor since they are all the same types for each "derived" CountedA/B types.
Anyways you can embed the resulting types into a std::tuple as shown in the link below (see Message class for an example). Then create a variadic template function similar to the applyTuple version in the link below that will add all your integer arguments and return the final result once all arguments have been unrolled. As for the returning of the enum value for the item in "Pos" simply call the get( tuple ).getEnum() or .value to get it.
How do I expand a tuple into variadic template function's arguments?

Related

Class template specialization that changes only one member function

I have a class template Function that takes a unsigned integer as a template argument, for the number of inputs. This template overloads operator() so the Function can be evaluated for a set of given inputs.
Usually, one of the prototypes for this member would be operator()(double, ...). However, if the template argument is 0, then that prototype wouldn't work, as it requires at least one argument.
template <unsigned Arity>
struct Function {
void operator () (double, ...);
};
Normally, I'd just write a template specialization, but there would be a lot of redundant code since there are a lot of other member functions. Again, normally, I'd make a base class containing the redundant code for the main class definition and the specialization to inherit from.
struct FunctionBase {
// Common code
Function operator + (Function const &) const; // ?
};
template <unsigned Arity>
struct Function : FunctionBase { /* etc */ };
Unfortunately, I'm unsure how to go about doing this, since for example operator+ is meant to return a Function. But how can it do this if Function is only defined later on? Function inherits from the base class, and by this design operator+ is in the base class...
It could return an instance of the base class, but then we need a way to convert that instance to an instance of Function, and I know of no way to do this without copying the first instance's data, which is very expensive in terms of performance.
How can I accomplish this?
The question is quite difficult to answer for it's far from being clear.
Below two possibile alternatives that try to address your issues:
If you want to go ahead with Arity template parameter, you can use sfinae'd operators to deal with Arity equal to 0:
#include<iostream>
template<int Arity>
struct Function {
template<int N = Arity>
std::enable_if_t<N == 0> operator()() {
std::cout << "arity == 0" << std::endl;
}
template<int N = Arity>
std::enable_if_t<N != 0> operator()(double, ...) {
std::cout << "arity != 0" << std::endl;
}
};
int main() {
Function<0> f1;
Function<2> f2;
f1();
f2(0., 42);
}
This way you no longer need to introduce a base class and all the related problems don't apply anymore.
If you mind changing approach instead, you can switch to the following pattern for your function object:
template<typename>
struct Function;
template<typename R, typename... A>
struct Function<R(A...)> {
R operator()(A... args) {
// ...
}
// ...
};
You can use it as it follows:
Function<void(int, char)> f;
If you want to have a fixed double as you first parameter for operator(), you can do this:
template<typename R, typename... A>
struct Function<R(double, A...)> {
R operator()(double d, A... args) {
// ...
}
// ...
};
And use it as it follows:
Function<void(double, int, char)> f1;
Function<void(double)> f1;
This will help at least dealing easily with empty parameter packs (note that sizeof...(A) will return you the number of submitted parameters in any case).
It follows a minimal, working example implementation:
#include<iostream>
template<typename>
struct Function;
template<typename R, typename... A>
struct Function<R(A...)> {
R operator()(A... args) {
int _[] = { 0, (std::cout << args << std::endl, 0)... };
(void)_;
}
template<typename... O>
Function<R(A..., O...)> operator+(Function<R(O...)>) {
return {};
}
// ...
};
int main() {
Function<void(int)> f1;
Function<void(double)> f2;
f1(42);
f2(0.);
(f1+f2)(3, .3);
}

Template template non-type parameter

I am not really sure if there's such a feature in C++, and if there is I can't seem to make it work, so I decided to ask. Can I have a template template non-type parameter. Something like this:
template<template<int> class A, int num>
class C
{
private:
A<num> obj;
};
My main issue is that I want to have a class C that accepts 2 classes as template parameters. Both of these classes specialize over a non-type parameter - say A<5>, B<5> and I want to pass them to class C which accepts two classes as template parameters. I need to make sure however that both of these classes specialize over the same non-type parameter - for example A<3>, B<4> shouldn't be allowed as arguments to class C.
A similar approach would be to do something like this:
template<int I>
class A {};
template<int I>
class B {};
// Forward declaration.
template<typename T, typename U>
class C;
template<template<int> class TA, template<int> class TB, int I, int J>
class C<TA<I>, TB<J>> {
// J exists only to make sure integer parameters match.
static_assert((I == J), "Template parameters' integer parameters are mismatched.");
private:
TA<I> objA;
TB<I> objB;
public:
// ...
};
// ...
C<A<4>, B<4>> ca4b4; // Valid.
C<A<4>, B<16>> ca4b16; // Static assert fails.
This will allow you to guarantee that both containers have the same integer parameter, and emit a readable error message if they don't.
Edit: Note that if you don't use two separate integer parameters and manually check for equality, attempting to create an instance with mismatching template template parameters will give off a less understandable "incomplete type" error message.
template<template<int> class TA, template<int> class TB, int I>
class C<TA<I>, TB<I>> {
// ...
};
// ...
C<A<4>, B<16>> ca4b16; // Oops.
/* Error messages:
* GCC:
* error: aggregate 'C<A<4>, B<16> > ca4b16' has incomplete type and cannot be defined
* C<A<4>, B<16>> ca4b16;
* ^
* MSVC:
* error C2079: 'ca4b16' uses undefined class 'C<T, U>'
* with
* [
* T=A<4>,
* U=B<16>
* ]
*/
This is because the actual definition will only be able to catch instances where both parameters have the same integer parameter, and any usage with mismatching integer parameters will instead fall back on the forward declaration, which is incomplete. Using two separate integer parameters, then manually checking for equality, allows us to catch bad instantiations instead of letting them fall back on the declaration, so we can convert that generic error into something that actually tells you what the problem is.
You may want to simplify your code with a "makeC":
#include <iostream>
template <template<int> class A, template<int> class B, int N>
class C {
A<N> a;
B<N> b;
};
using namespace std;
template <template<int> class A, template<int> class B, int N>
C<A,B,N> makeC(A<N> a, B<N> b) {
return C<A, B, N>{};
}
template<int N>
class AImpl {
};
template<int N>
class BImpl {
};
int main() {
auto c = makeC(AImpl<2>{}, BImpl<2>{});
//auto error = makeC(AImpl<1>{}, BImpl<2>{});
return 0;
}
My bad, after some tinkering I managed to call it right. The issue was I was calling C<A<5>> and i needed to obviously call C<A, 5>. Here's an example of how I made the whole thing work just in case if anybody ever needs it:
template<int a, int b, int c>
class Anac
{
public:
void print()
{
std::cout << "Anac: " << a << " " << b << " " << c << "\n";
}
};
template<int a, int b, int c>
class Mamut
{
public:
void print()
{
std::cout << "Mamut: " << a << " " << b << " " << c << "\n";
}
};
template <class C>
class R
{
};
template< template<int, int, int> class A, template<int, int, int> class B, int a, int b, int c>
class gm
{
private:
A<a,b,c> p1;
B<a,b,c> p2;
public:
void print()
{
p1.print();
p2.print();
}
};
int main()
{
gm<Anac, Mamut, 3, 4, 5> game;
game.print();
std::cin.ignore();
return 0;
}

Inner typedef changes depending on parent class

I give up, please help explain this behaviour. The example I present below is the simplest one I could think of, but it sums up the problem (using g++ 4.9.2 on Cygwin with c++14 enabled). I want to create a class which will behave similar to std::mem_fn. Here is my class:
template <class R, class T, R(T::*P)() const >
struct property {
static R get(const T& t) {
return (t.*P)();
}
};
where R is the return type and T is the type of the object I am interesting in. The third template parameter is a pointer to member function. So far, so good.
I then create a simple class which holds an integer as follows
class data_class {
public:
unsigned get_data() const {
return m_data;
}
private:
unsigned m_data;
};
This is the class which will be used in the property class shown before.
Now I create two classes which inherit from data_class as follows
struct my_classA
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
//same as my_classA, only templated
template <int I>
struct my_classB
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
They have the exact same inner typedef, but my_classB is templated. Now the following types should in theory be the same:
using target_t = property<unsigned, data_class, &data_class::get_data>;
using test1_t = typename my_classA::data;
using test2_t = typename my_classB<1>::data;
However my compiler says that only test1_t and target_t are the same. The type deduced for test2_t is apparently
property<unsigned int, data_class, (& data_class::get_data)> >
where this type has these brackets around the pointer to member function. Why test2_t is not the same as target_t? Here is the full code in case you want to try it on your system. Any help is much appreciated.
#include <type_traits>
class data_class {
public:
unsigned get_data() const {
return m_data;
}
private:
unsigned m_data;
};
//takes return type, class type, and a pointer to member function
//the get function takes an object as argument and uses the above pointer to call the member function
template <class R, class T, R(T::*P)() const >
struct property {
static R get(const T& t) {
return (t.*P)();
}
};
struct my_classA
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
//same as my_classA, only templated
template <int I>
struct my_classB
: public data_class {
using data = property<unsigned, data_class, &data_class::get_data>;
};
//used to produce informative errors
template <class T>
struct what_is;
//all 3 types below should, in theory, be the same
//but g++ says that test2_t is different
using target_t = property<unsigned, data_class, &data_class::get_data>;
using test1_t = typename my_classA::data;
using test2_t = typename my_classB<1>::data;
static_assert(std::is_same<target_t, test1_t>::value, ""); //this passes
static_assert(std::is_same<target_t, test2_t>::value, ""); //this does not
int main() {
what_is<test1_t> t1;
what_is<test2_t> t2;
}
I ran your code with c++11 because I'm not very familiar with c++14 yet. But all I replaced were the using (aliases) with typedefs and simplified the code a little bit. Nothing to affect its output.
I got the desired results by adding a typename T to the inherited classB template which when instantiated, it will replace the R with T, so in this case "unsigned".
#include <iostream>
#include <type_traits>
template <typename R, typename T, R(T::*P)() const>
struct property
{
static R get(const T& t)
{
return (t.*P)();
}
};
struct data_class
{
private:
unsigned m_data;
public:
unsigned get_data() const
{
return m_data;
}
};
struct my_classA : public data_class
{
typedef property<unsigned, data_class, &data_class::get_data> data;
};
template <typename T, int>
struct my_classB : public data_class
{
typedef property<T, data_class, &data_class::get_data> data;
};
int main()
{
typedef typename my_classA::data normClassA;
typedef typename my_classB<unsigned,1>::data tmplClassB;
std::cout<< std::is_same< property<unsigned, data_class, &data_class::get_data> , normClassA >::value <<std::endl;
std::cout<< std::is_same< property<unsigned, data_class, &data_class::get_data> , tmplClassB >::value <<std::endl;
}
The result is this:
~$g++ -std=c++11 test.cpp
~$./a.out
1
1
I think the problem has to do with the class template instantiation criteria because when I originally tried to print the sizeof's of the two classes, my_classA::data returned 1, but my_classB<1>::data ended in a compiller error. I'm still quite fuzzy as to why this occurs. Technically it should have instantiated the class template just fine. Maybe it's the property inside the classB template that was falsely instantiated. I'll look more into this but if you find the answer, please post it. It's an interesting one!
EDIT:
The original code works fine on Cygwin GCC 4.8.2. Result is 1 and 1. Maybe it's just a gcc4.9.2 compiler issue.

Function array initialization at compile time with metaprograming

In video-games is common that resources are loaded in a step fashion way, so within a single thread a loading bar can update at each loading step. By example:
1 -> Load texture A
2 -> Update Loading Bar to 2%
3 -> Load texture B
4 -> Update Loading Bar to 4%
5 ...
This can be done in many ways. One of these is define a function for each loading step.
void LoadTextureA()
{
//Loading routine
...
}
This has the advantage of readability, not need too much nested code and even possible in some cases to share loading routines between two game states.
Now what I was thinking was to generalize this "function-for-step" model with templates. Lets say.
template <int S>
struct Foo{
void LoadingStep()
{
}
};
template <>
struct Foo<0>
{
void LoadingStep()
{
//First loading step
...
}
};
Please correct me if I'm wrong. But it appears possible that I can compile-time iterate through 0 .. to N steps using metaprogramming and assign this specialized functions to an array or vector of function pointers.
N steps are known at compile time along with it respective functions.
Function pointer vector would be iterated like this:
template <int Steps>
class Loader {
public:
bool Load()
{
functionArray[m_step]();
if (++m_step == Steps)
return false; //End loading
else
return true;
}
private:
int m_step;
}
Is this possible? I know that that are easier ways to do it. But besides project requirments it's an interesting programming challenge
I achieved it based on Kal answer of a similar problem
Create N-element constexpr array in C++11
template <int S>
struct Foo{
static void LoadingStep()
{
}
};
template <>
struct Foo<0>
{
static void LoadingStep()
{
//First loading step
}
};
template<template<int S> class T,int N, int... Rest>
struct Array_impl {
static constexpr auto& value = Array_impl<T,N - 1, N, Rest...>::value;
};
template<template<int S> class T,int... Rest>
struct Array_impl<T,0, Rest...> {
static constexpr std::array<void*,sizeof...(Rest)+1> value = {reinterpret_cast<void*>(T<0>::LoadingStep),reinterpret_cast<void*>(T<Rest>::LoadingStep)...};
};
template<template<int S> class T,int... Rest>
constexpr std::array<void*,sizeof...(Rest)+1> Array_impl<T,0, Rest...>::value;
template<template<int S> class T,int N>
struct F_Array {
static_assert(N >= 0, "N must be at least 0");
static constexpr auto& value = Array_impl<T,N>::value;
F_Array() = delete;
F_Array(const F_Array&) = delete;
F_Array(F_Array&&) = delete;
};
Using example:
int main()
{
auto& value = F_Array< Foo ,4>::value;
std::cout << value[0] << std::endl;
}
This yields of void* array of pointers to template functions:
Foo<0>::LoadinStep()
Foo<1>::LoadinStep()
Foo<2>::LoadinStep()
Foo<3>::LoadinStep()
Foo<4>::LoadinStep()
Since Foo<1..3> are not specialized they will fall to Default LoadingStep function
Yes. It's possible. And if you use the template metaprogramming, you don't need to use a run time loop, but a recursive call to a template method:
#include <iostream>
// The template numerated methods
template <int S> struct Foo{static void LoadingStep(){}};
template <> struct Foo<0> {static void LoadingStep(){std::cout<<0;}};
template <> struct Foo<1> {static void LoadingStep(){std::cout<<1;}};
template <> struct Foo<2> {static void LoadingStep(){std::cout<<2;}};
// The loader template method
template <int Step>
void Loader()
{
Foo<Step>::LoadingStep();
Loader<Step-1>();
}
// Stopping rule
template <> void Loader<-1>(){}
int main()
{
Loader<2>();
}
If you want an array:
LoadingFunction functionArray[] = {Function0, Function1, Function2};
.....
for (int i = 0; i < nSteps; ++i)
RunStep(i, nSteps, Function[i]);
Or initialize an std container with it.
If you want templates, you could write
for (int i = 0; i < nSteps; ++i)
RunStep(i, nSteps, Function<i>);
except i in Function<i> must be a constant. So you have to do it with a templated recursive something:
template <int i, int NSteps> struct RunSteps
{
void Run()
{
RunStep(i, NSteps, Function<i>);
RunSteps<i+1, NSteps>::Run();
}
};
template <int NSteps> struct RunSteps<NSteps, NSteps>
{
void Run() {}
};
RunSteps<0, NSteps>::Run();
Compile-time iteration doesn't really exist. The for loop and the templated recursive something do exactly the same thing. The compiler is as capable of unrolling a loop, as of inlining a call.
It looks like there's very little to be gained from templatizing this stuff, and lots to lose.
It is not clear why you would want to put templated functions to an array at compile time, but here you go:
LoadingFunction functionArray[] = {Function<0>, Function<1>, Function<2>};
Now if you don't want to enumerate functions manually like that, it could be a bit of a challenge. It doesn't seem possible with either legacy C arrays or any of the std containers. Assuming you really need it, it's possible to write a custom container capable of such initialization.
template <template <int> class FunctionWrappper, int NFunctions>
class MyOptimizedFunctionArray {
// filling this space is left as an exercise
};

Template metaprogram converting type to unique number

I just started playing with metaprogramming and I am working on different tasks just to explore the domain. One of these was to generate a unique integer and map it to type, like below:
int myInt = TypeInt<AClass>::value;
Where value should be a compile time constant, which in turn may be used further in meta programs.
I want to know if this is at all possible, and in that case how. Because although I have learned much about exploring this subject I still have failed to come up with an answer.
(P.S. A yes/no answer is much more gratifying than a c++ solution that doesn't use metaprogramming, as this is the domain that I am exploring)
In principle, this is possible, although the solution probably isn't what you're looking for.
In short, you need to provide an explicit mapping from the types to the integer values, with one entry for each possible type:
template< typename T >
struct type2int
{
// enum { result = 0 }; // do this if you want a fallback value
};
template<> struct type2int<AClass> { enum { result = 1 }; };
template<> struct type2int<BClass> { enum { result = 2 }; };
template<> struct type2int<CClass> { enum { result = 3 }; };
const int i = type2int<T>::result;
If you don't supply the fallback implementation in the base template, this will fail for unknown types if T, otherwise it would return the fallback value.
Depending on your context, there might be other possibilities, too. For example, you could define those numbers within within the types themselves:
class AClass {
public:
enum { inta_val = 1 };
// ...
};
class BClass {
public:
enum { inta_val = 2 };
// ...
};
// ...
template< typename T >
struct type2int
{
enum { result = T::int_val }; // will fail for types without int_val
};
If you give more context, there might be other solutions, too.
Edit:
Actually there isn't any more context to it. I was looking into if it actually was possible, but without assigning the numbers itself.
I think Mike's idea of ordering is a good way to do this (again, for a fixed set of types) without having to explicitly assign numbers: they're implicitly given by the ordering. However, I think that this would be easier by using a type list. The index of any type in the list would be its number. I think something like the following might do:
// basic type list manipulation stuff
template< typename T1, typename T2, typename T3...>
struct type_list;
// meta function, List is assumed to be some instance of type_list
template< typename T, class List >
struct index_of {
enum { result = /* find index of T in List */ };
};
// the list of types you support
typedef type_list<AClass, BClass, CClass> the_type_list;
// your meta function
template< typename T >
struct type2int
{
enum { result = index_of<T, the_type_list>::result };
};
This does what you want. Values are assigned on need. It takes advantage of the way statics in functions are assigned.
inline size_t next_value()
{
static size_t id = 0;
size_t result = id;
++id;
return result;
}
/** Returns a small value which identifies the type.
Multiple calls with the same type return the same value. */
template <typename T>
size_t get_unique_int()
{
static size_t id = next_value();
return id;
}
It's not template metaprogramming on steroids but I count that as a good thing (believe me!)
Similiar to Michael Anderson's approach but this implementation is fully standards compliant and can be performed at compile time. Beginning with C++17 it looks like constexpr values will be allowed to be used as a template parameter for other template meta programming purposes. Also unique_id_type can be compared with ==, !=, >, <, etc. for sorting purposes.
// the type used to uniquely identify a list of template types
typedef void (*unique_id_type)();
// each instantiation of this template has its own static dummy function. The
// address of this function is used to uniquely identify the list of types
template <typename... Arguments>
struct IdGen {
static constexpr inline unique_id_type get_unique_id()
{
return &IdGen::dummy;
}
private:
static void dummy(){};
};
The closest I've come so far is being able to keep a list of types while tracking the distance back to the base (giving a unique value). Note the "position" here will be unique to your type if you track things correctly (see the main for the example)
template <class Prev, class This>
class TypeList
{
public:
enum
{
position = (Prev::position) + 1,
};
};
template <>
class TypeList<void, void>
{
public:
enum
{
position = 0,
};
};
#include <iostream>
int main()
{
typedef TypeList< void, void> base; // base
typedef TypeList< base, double> t2; // position is unique id for double
typedef TypeList< t2, char > t3; // position is unique id for char
std::cout << "T1 Posn: " << base::position << std::endl;
std::cout << "T2 Posn: " << t2::position << std::endl;
std::cout << "T3 Posn: " << t3::position << std::endl;
}
This works, but naturally I'd like to not have to specify a "prev" type somehow. Preferably figuring out a way to track this automatically. Maybe I'll play with it some more to see if it's possible. Definitely an interesting/fun puzzle.
I think it is possible to do it for a fixed set of types, but quite a bit of work. You'll need to define a specialisation for each type, but it should be possible to use compile-time asserts to check for uniqueness. I'll assume a STATIC_ASSERT(const_expr), like the one in Boost.StaticAssert, that causes a compilation failure if the expression is false.
Suppose we have a set of types that we want unique IDs for - just 3 for this example:
class TypeA;
class TypeB;
typedef int TypeC;
We'll want a way to compare types:
template <typename T, typename U> struct SameType
{
const bool value = false;
};
template <typename T> struct SameType<T,T>
{
const bool value = true;
};
Now, we define an ordering of all the types we want to enumerate:
template <typename T> struct Ordering {};
template <> struct Ordering<void>
{
typedef TypeC prev;
typedef TypeA next;
};
template <> struct Ordering<TypeA>
{
typedef void prev;
typedef TypeB next;
};
template <> struct Ordering<TypeB>
{
typedef TypeA prev;
typedef TypeC next;
};
template <> struct Ordering<TypeC>
{
typedef TypeB prev;
typedef void next;
};
Now we can define the unique ID:
template <typename T> struct TypeInt
{
STATIC_ASSERT(SameType<Ordering<T>::prev::next, T>::value);
static int value = TypeInt<T>::prev::value + 1;
};
template <> struct TypeInt<void>
{
static int value = 0;
};
NOTE: I haven't tried compiling any of this. It may need typename adding in a few places, and it may not work at all.
You can't hope to map all possible types to an integer field, because there are an unbounded number of them: pointer types with arbitrary levels of indirection, array types of arbitrary size and rank, function types with arbitrary numbers of arguments, and so on.
I'm not aware of a way to map a compile-time constant integer to a type, but I can give you the next best thing. This example demonstrates a way to generate a unique identifier for a type which - while it is not an integral constant expression - will generally be evaluated at compile time. It's also potentially useful if you need a mapping between a type and a unique non-type template argument.
struct Dummy
{
};
template<typename>
struct TypeDummy
{
static const Dummy value;
};
template<typename T>
const Dummy TypeDummy<T>::value = Dummy();
typedef const Dummy* TypeId;
template<typename T, TypeId p = &TypeDummy<T>::value>
struct TypePtr
{
static const TypeId value;
};
template<typename T, TypeId p>
const TypeId TypePtr<T, p>::value = p;
struct A{};
struct B{};
const TypeId typeA = TypePtr<A>::value;
const TypeId typeB = TypePtr<B>::value;
I developed this as a workaround for performance issues with ordering types using typeid(A) == typeid(B), which a certain compiler fails to evaluate at compile time. It's also useful to be able to store TypeId values for comparison at runtime: e.g. someType == TypePtr<A>::value
This may be doing some "bad things" and probably violates the standard in some subtle ways... but thought I'd share anyway .. maybe some one else can sanitise it into something 100% legal? But it seems to work on my compiler.
The logic is this .. construct a static member function for each type you're interested in and take its address. Then convert that address to an int. The bits that are a bit suspect are : 1) the function ptr to int conversion. and 2) I'm not sure the standard guarantees that the addresses of the static member functions will all correctly merge for uses in different compilation units.
typedef void(*fnptr)(void);
union converter
{
fnptr f;
int i;
};
template<typename T>
struct TypeInt
{
static void dummy() {}
static int value() { converter c; c.f = dummy; return c.i; }
};
int main()
{
std::cout<< TypeInt<int>::value() << std::endl;
std::cout<< TypeInt<unsigned int>::value() << std::endl;
std::cout<< TypeInt< TypeVoidP<int> >::value() << std::endl;
}
I don't think it's possible without assigning the numbers yourself or having a single file know about all the types. And even then you will run into trouble with template classes. Do you have to assign the number for each possible instantiation of the class?
type2int as compile time constant is impossible even in C++11. Maybe some rich guy should promise a reward for the anwser? Until then I'm using the following solution, which is basically equal to Matthew Herrmann's:
class type2intbase {
template <typename T>
friend struct type2int;
static const int next() {
static int id = 0; return id++;
}
};
template <typename T>
struct type2int {
static const int value() {
static const int id = type2intbase::next(); return id;
}
};
Note also
template <typename T>
struct type2ptr {
static const void* const value() {
return typeid(T).name();
}
};