Changing enum values based on HW selection - c++

What is the best way to design a function where the allowed enum value changes based on the HW selected for example. The function might look like this, this won't compile but hopefully you get the idea.
enum class EHWUnit
{
Unit0,
Unit1,
Unit2
};
enum class EHWType
{
Type1,
Type2,
Type3,
Type4
};
SelectHWinput( const EHWUnit aUnit, const EHWType aHWType, const unit8_t aSelect )
{
if( aHWType & 1 == 0)
ArrayIS( 2,4,6,8,10)
else
ArrayIS( 1,3,5,7,9)
element = ArrayIS(aSelect)
WriteToRegister( Address, bits, element);
}
Does anyone know of a way to make it better to select the element based on type? I would prefer to somehow link those even elements to the type at compile time and not select them at run time, using one function is there anyway to make this code more fool proof?

A shot in the dark, as I'm not sure what exactly it is you want to do.
It seems like you want to make some compile-time decisions, which could look something like this:
enum EHWUnit
{
Unit0,
Unit1,
Unit2
};
enum EHWType
{
Type1,
Type2,
Type3,
Type4
};
template <int Select>
struct ArrayIS
{
static constexpr int const array[] = {1, 3, 5, 7, 9};
};
template <int Select>
constexpr int const ArrayIS<Select>::array[];
template <>
struct ArrayIS<0>
{
static constexpr int const array[] = {2, 4, 6, 8, 10};
};
constexpr int const ArrayIS<0>::array[];
template <EHWUnit aUnit, EHWType aHWType, int aSelect>
struct SelectHWinput
{
static constexpr int get()
{
return ArrayIS<static_cast<int>(aHWType) & 1>::array[aSelect];
}
};
int main()
{
// To illustrate that the value is selected at compile-time, a
// static assertion that will always fail:
static_assert(SelectHWinput<Unit0, Type1, 0>::get() < 0, "Works, value selected at compile-time");
}
In the code above, Template Metaprogramming (TMP) is used to select a value of one of the arrays at compile-time. The correct array is chosen by supplying the Select template non-type parameter. Because these arrays are declared constexpr, a constexpr member-function can be used to extract one of its element at compile-time. The static_assert proves that it works.
If you want to construct the arrays more flexibly at compile-time, I suggest looking at this question of mine, where I describe TMP-functors to construct static arrays at compile-time:
g++ (4.7.2) bug or feature, when initializing static arrays at compile-time?
EDIT:
You said you were interested in the Array implementation:
template <typename T, size_t Count, template <typename, T> class Function, T Current, T ... Elements>
struct Array_: public Array_<T, Count - 1, Function, Function<T, Current>::result, Elements ..., Current>
{};
template <typename T, template <typename, T> class Function, T Current, T ... Elements>
struct Array_<T, 0, Function, Current, Elements...>
{
constexpr static std::array<T, sizeof...(Elements)> array = {{Elements ...}};
};
template <typename T, size_t Count, T Start = T(), template <typename, T> class Function = Null>
struct Array: public Array_<T, Count, Function, Start>
{};
Here, the last one is the one you'll use, using a Template Metaprogramming functor that tells the compiler how to generate the next element from the previous one. This is a limitation, but it's the best I could come up with at the time.
Three simple functors that you could use:
template <typename T, T Value>
struct Null
{
enum { result = Value };
};
template <typename T, T Value>
struct Increment
{
enum { result = Value + 1 };
};
template <typename T, T Value>
struct Decrement
{
enum { result = Value - 1 };
};
For example, if you need an array with incremental values from 1 to 100, you would do this:
std::array<int, 100> arr = Array<int, 100, 1, Increment>::array;
This array is then allocated and populated at compile-time.

Related

C++20 How to get the last element of a parameter pack of std::size_t

I've seen many answers online such as this one, but they do not seem to work when the parameter pack is of std::size_t.
template <typename ...Ts>
struct select_last
{
using type = typename decltype((std::type_identity<Ts>{}, ...))::type;
};
template<std::size_t... N>
class Example {
private:
using type = select_last<int, double>::type; // works
using size_t_type = select_last<size_t... N>::type; // doesn't work
};
How can I get the last element of a parameter pack of type std::size_t?
The template<std::size_t... N> is based on a non-type template parameter, so you cannot extract the type (or more precisely, there is no sense in trying to extract the type - I can just tell you it is std::size_t!), you may however extract the value, into a static constexpr.
Here is the proposed code:
template<std::size_t... N>
struct Last {
static constexpr std::size_t val = (N, ...); // take the last
};
int main() {
std::cout << Last<1, 2, 3, 99>::val; // 99
}
If you want, you can actually have it work for any type:
template<auto... N>
struct Last {
static constexpr auto val = (N, ...); // take the last
};

How do I get access to template parameters of a template pack parameter

I am trying to create a template class that will enable a compare function to return an integer [ 0 - equal, >0 a should come first, <0 b should come first ].
I am using Sort structs template parameters to keep track of the type that should be used, offset of the field in the string, as well as the order that this field should be kept... so compare can according return the correct value.
Assume for now that the std::string is used to represent a serialized value.
I am having trouble with extracting the information from the template. I have kept sort as a pack parameter, which would be of the type Sort. How do I access these parameters in the code? If there is a better way to refactor this. I looked at some of the other questions related to templates, but didn't see any that would solve this problem. I am using gcc 8.2 and c++17.
#include <cstdint>
#include <string>
#include <cstring>
#include <cassert>
template<typename T, uint32_t offset, char Order = 'A'>
struct Sort {};
template<uint32_t keyLength, template<typename T,uint32_t offset, char Order> class ... sort>
class Comparator {
public:
int compare(std::string & a, std::string &b) {
assert(a.length()==b.length());
// How would I sum the sizeof each T. i.e. if T is int and another T is short, then sum should be 6+keyLength?
assert(a.length()==(sizeof(T)+keyLength)); // Check that my length is equal to key length + all type lengths put together
auto r = memcmp(a.data(),b.data(),keyLength);
if(r!=0) return r;
// How do I retrieve T,offset,Order of each pack parameter.
return internal_compare<T,offset,Order>(a.data(),b.data())? internal_compare<T,offset,Order>(a.data(),b.data()) : ...;
}
private:
template<typename IT,uint32_t iOffset, char iOrder>
int internal_compare(char * a,char *b) {
if constexpr (iOrder=='A'||iOrder=='a') {
return (*(static_cast<IT *>(a+iOffset)))-(*(static_cast<IT *>(b+iOffset)));
} else {
return (*(static_cast<IT *>(b+iOffset)))-(*(static_cast<IT *>(a+iOffset)));
}
}
};
Two things I have not been able to accomplish.
One is getting the sum of sizeof(T) from the sort.
Call the internal compare operator on each sort.
Link to code on compiler explorer
This becomes substantially easier if instead of using this form:
template<typename T, uint32_t offset, char Order = 'A'>
struct Sort {};
template<uint32_t keyLength, template<typename T,uint32_t offset, char Order> class ... sort>
class Comparator;
You use this one:
template <uint32_t keyLength, class...>
class Comparator;
template <uint32_t keyLength, typename... T, uint32_t... offset, char... Order>
class Comparator<keyLength, Sort<T, offset, Order>...> {
// ...
};
First, the original didn't do what you wanted to do anyway. You wanted specific instantiations of Sort but you were actually accepting class templates... like Comparator<32, Sort, Sort, Sort>. Which presumably isn't meaningful.
But when we do it this way, we're not only accepting only instantiations of Sort but we have the parameters in the most useful form. So something like this:
// How would I sum the sizeof each T. i.e. if T is int and another T is short,
// then sum should be 6+keyLength?
Is a fold-expression:
(sizeof(T) + ... + keyLength)
And so forth.
I'll take this problem on another front: how do you extract the template parameters if T has template parameters? Here's an example:
template<typename T>
void foo(T v) {
// T is std::vector<int>, how to extract `int`?
}
int main() {
foo(std::vector{1, 2, 3, 4});
}
There's many answers to that: extraction using partial specialization, type aliases and others.
Here's how you can do it for std::vector:
template<typename>
struct extract_value_type_t {};
template<typename T>
struct extract_value_type_t<std::vector<T>> {
using type = T;
};
template<typename T>
using extract_value_type_t = typename extract_value_type<T>::type;
template<typename T>
void foo(T v) {
// with template specialization
using value_type = extract_value_type_t<T>;
// with the member alias std::vector exposes
// needs much less boilerplate!
using value_type = typename T::value_type;
}
What does doing it with T when it's a vector gives us? Well, if you can do something with a simple type T, you won't even need a template template parameter, making your interface more flexible:
template<typename>
struct sort_traits {};
template<typename T, uint32_t offset_, char order_>
struct sort_traits<Sort<T, offset_, order_>> {
using type = T
static constexpr auto offset = offset_;
static constexpr auto order = order_;
};
Then in your Comparator class, simply do something like that:
template<uint32_t keyLength, typename... sorts>
struct Comparator {
int compare(std::string const& a, std::string const& b) {
return (internal_compare<sorts>(a.data(), b.data()) && ...);
}
private:
template<typename sort>
int internal_compare(char const* a, char const* b) {
using traits = sort_traits<sort>;
using type = typename traits::type;
constexpr auto offset = traits::offset;
constexpr auto order = traits::order;
// do stuff
}
};
This also add the possibility one day to add another kind of sort that would have different template parameters or different things exposed.

Insert a transformed integer_sequence into a variadic template argument?

How do you insert a transformed integer_sequence (or similar since I am targeting C++11) into a variadic template argument?
For example I have a class that represents a set of bit-wise flags (shown below). It is made using a nested-class because you cannot have two variadic template arguments for the same class. It would be used like typedef Flags<unsigned char, FLAG_A, FLAG_B, FLAG_C>::WithValues<0x01, 0x02, 0x04> MyFlags. Typically, they will be used with the values that are powers of two (although not always, in some cases certain combinations would be made, for example one could imagine a set of flags like Read=0x1, Write=0x2, and ReadWrite=0x3=0x1|0x2). I would like to provide a way to do typedef Flags<unsigned char, FLAG_A, FLAG_B, FLAG_C>::WithDefaultValues MyFlags.
template<class _B, template <class,class,_B> class... _Fs>
class Flags
{
public:
template<_B... _Vs>
class WithValues :
public _Fs<_B, Flags<_B,_Fs...>::WithValues<_Vs...>, _Vs>...
{
// ...
};
};
I have tried the following without success (placed inside the Flags class, outside the WithValues class):
private:
struct _F {
// dummy class which can be given to a flag-name template
template <_B _V> inline constexpr
explicit _F(std::integral_constant<_B, _V>) { } };
// we count the flags, but only in a dummy way
static constexpr unsigned _count = sizeof...(_Fs<_B, _F, 1>);
static inline constexpr
_B pow2(unsigned exp, _B base = 2, _B result = 1) {
return exp < 1 ?
result :
pow2(exp/2,
base*base,
(exp % 2) ? result*base : result);
}
template <_B... _Is> struct indices {
using next = indices<_Is..., sizeof...(_Is)>;
using WithPow2Values = WithValues<pow2(_Is)...>;
};
template <unsigned N> struct build_indices {
using type = typename build_indices<N-1>::type::next;
};
template <> struct build_indices<0> {
using type = indices<>;
};
//// Another attempt
//template < _B... _Is> struct indices {
// using WithPow2Values = WithValues<pow2(_Is)...>;
//};
//template <unsigned N, _B... _Is> struct build_indices
// : build_indices<N-1, N-1, _Is...> { };
//template < _B... _Is> struct build_indices<0, _Is...>
// : indices<_Is...> { };
public:
using WithDefaultValues =
typename build_indices<_count>::type::WithPow2Values;
Of course, I would be willing to have any other alternatives to the whole situation (supporting both flag names and values in the same template set, etc).
I have included a "working" example at ideone: http://ideone.com/NYtUrg - by "working" I mean compiles fine without using default values but fails with default values (there is a #define to switch between them).
Thanks!
So I figured it out on my own, I guess I posted too soon.
I was able to get around the error generated with the given code with a dummy template argument in the build_indices and indices template classes above. The argument has to be a typename as they currently have variadic integral types.
(Note: still using the improper _F names here - I have corrected my personal code to not use these reserved name - thanks for the tip)
Here is a working solution which results in the WithValues<...> template to be filled with powers of 2 (1, 2, 4, 8, 16, ...) based on the size of the Flags variadic template.
private:
// dummy class which can be given to a flag-name template
struct _F
{
template <_B _V> inline constexpr
explicit _F(std::integral_constant<_B, _V>) { }
};
// we count the flags, but only in a dummy way
static constexpr unsigned _count = sizeof...(_Fs<_B, _F, 1>);
static inline constexpr
_B pow2(unsigned exp, _B base = 2, _B result = 1)
{
return exp < 1 ?
result :
pow2(exp/2, base*base, (exp % 2) ? result*base : result);
}
template <class dummy, _B... _Is> struct indices
{
using next = indices<dummy, _Is..., sizeof...(_Is)>;
using WithPow2Values = WithValues<pow2(_Is)...>;
};
template <class dummy, unsigned N> struct build_indices
{
using type = typename build_indices<dummy, N-1>::type::next;
};
template <class dummy> struct build_indices<dummy, 0>
{
using type = indices<dummy>;
};
public:
using WithDefaultValues =
typename build_indices<void, _count>::type::WithPow2Values;

compile-time counter for template classes

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!)

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();
}
};