Vittorio Romeo
Vittorio Romeo

Reputation: 93274

Compile-time template `std::integral_constant` counter - how to implement it?

I have several types and I want to "bind" an std::integral_constant sequential ID value to every type at compile-time.

Example:

struct Type00 { };
struct Type01 { };
struct Type02 { };
struct Type03 { };
struct TypeXX { };
struct TypeYY { };

template<typename T> struct TypeInfo
{
    using Id = std::integral_constant<int, ???>;
};

int main()
{
     cout << TypeInfo<Type00>::Id::value; // Should always print 0
     cout << TypeInfo<Type01>::Id::value; // Should always print 1
     cout << TypeInfo<Type02>::Id::value; // Should always print 2
     cout << TypeInfo<Type03>::Id::value; // Should always print 3
     cout << TypeInfo<TypeXX>::Id::value; // Should always print 4
     cout << TypeInfo<TypeYY>::Id::value; // Should always print 5
}

The problem is that I don't know how to keep track of the last used ID. Ideally, I want something like:

template<typename T> struct TypeInfo
{
    using Id = std::integral_constant<int, lastUsedID + 1>;
};

Is there any way to define and keep track of a compile-time lastUsedID?

How can I solve this problem?


EDIT:

Clarifications:

Upvotes: 6

Views: 1337

Answers (2)

Daniel Frey
Daniel Frey

Reputation: 56863

It is not possible and it's quite easy to see why: Consider two compilation units. Unit one sees type Type00, but not Type01, while unit two sees Type01 but not Type00. There is nothing in C++ (including C++11 and C++14) which could now tell the compiler in both compilation units which order those types should have. Even adding some data to the object file for link-time is too late, as you are asking for a compile-time value. This is the fundamental concept of compilation units which poses a hard barrier for the feature you are asking for.

Upvotes: 3

Oktalist
Oktalist

Reputation: 14714

You could do this with a type list:

template <typename... Ts>
struct TypeList;

template <>
struct TypeList<>
{
    static const int size = 0;
    static std::integral_constant<int, -1> indexOf(...);
};

template <typename Head, typename... Tail>
struct TypeList<Head, Tail...> : TypeList<Tail...>
{
    static const int size = sizeof...(Tail) + 1;
    static std::integral_constant<int, sizeof...(Tail)> indexOf(Head&&);
    using TypeList<Tail...>::indexOf;
};

template <typename TypeList, typename T>
using IndexOf = std::integral_constant<int,
    TypeList::size - decltype(TypeList::indexOf(std::declval<T>()))::value - 1>;

If T is not present in List then IndexOf<List, T>::value is -1. You can cause this case to be a compile error instead, by removing the ellipsis from the signature TypeList<>::indexOf(...).

Usage:

struct Type00 { };
struct Type01 { };
struct Type02 { };
struct Type03 { };
struct TypeXX { };
struct TypeYY { };

using MyTypeList = TypeList<
    Type00,
    Type01,
    Type02,
    Type03,
    TypeXX,
    TypeYY
>;

int main()
{
    std::cout << IndexOf<MyTypeList, Type00>::value
              << IndexOf<MyTypeList, Type01>::value
              << IndexOf<MyTypeList, Type02>::value
              << IndexOf<MyTypeList, Type03>::value
              << IndexOf<MyTypeList, TypeXX>::value
              << IndexOf<MyTypeList, TypeYY>::value;
}

Demo

Upvotes: 1

Related Questions