Automatically assign index for every template specialization at compile time via C++ meta programming?

51 views Asked by At

For example, I want to implement a template function like this:

template<typename T>
int GetTypeIndex() {}

which always return a determinant zero-based index for the same 'T', and finally a method to get the total count of all 'T's at compile time (a constexpr or macro?)

struct A
{
};
struct B
{
};

GetTypeIndex<A>();// returns 0,
GetTypeIndex<B>();// returns 1,

size_t sizeTable[ALL_TYPE_COUNT]; //ALL_TYPE_COUNT equals to 2 hence GetTypeIndex has 2 specialization(A and B) 

I've already made it by pre-declare every 'T' with some macro wraps the __COUNTER__, but it's just not that convenient, and use __COUNTER__ in other place will corrupt the whole design.

I just want to get rid of those macro tricks.

Edit: My English is poor so I decide to put on what I made previously with macros to help understanding what I mean.

#define INDEXED_TYPE_DEFINE_BEGIN \
template<typename T> int GetTypeIndex() = delete;

#define INDEXED_TYPE_DEFINE_END \
constexpr int INDEXED_TYPE_COUNT = __COUNTER__;

#define AS_INDEXED_TYPE(T) T\
constexpr int Index_##T= __COUNTER__;\
template<> inline int GetTypeIndex<T>() { return Index_##T; }

and the user code would be like:

INDEXED_TYPE_DEFINE_BEGIN 

typedef struct
{
    float x;
    float y;
}
AS_INDEXED_TYPE(Position)

typedef struct
{
    float min;
    float max;
}
AS_INDEXED_TYPE(Range)

INDEXED_TYPE_DEFINE_END 

now GetTypeIndex<Position>() would return 0 GetTypeIndex<Range>() would return 1 INDEXED_TYPE_COUNT equals to 2 and I can use it at compile time but it's just very inconvenient.

1

There are 1 answers

1
Artyer On

It might be easier to just keep a single list of all types:

#include <tuple>
#include <utility>
#include <type_traits>
#include <cstddef>

using all_types = std::tuple<
    A, B, ...
>;  // These don't need to be complete, i.e., just forward declarations work

// Depending on your C++ version, you can use something from https://stackoverflow.com/q/41579477 instead
template<typename T>
inline constexpr std::size_t type_index = []<std::size_t... I>(std::index_sequence<I...>){
    std::size_t result = -1;
    ((std::is_same_v<T, std::tuple_element_t<I, all_types>> ? void(result = I) : void()), ...);
    if (result == -1) throw "type_index<T>: T not in all_types";
    return result;
}(std::make_index_sequence<std::tuple_size_v<all_types>>{});

template<std::size_t I>
using index_to_type = std::tuple_element_t<I, all_types>;

std::size_t sizeTable[std::tuple_size_v<all_types>];

static_assert(type_index<A> == 0);
static_assert(type_index<B> == 1);

https://godbolt.org/z/nr6v14Gaz

This doesn't need a macro but forces you to declare all your types in one place (they can't be piecemeal), but it sounds like you are already doing that.