In C++ template programming, sometimes I would like to declare a variable of certain type based on a query type. This requires creating a compile-time type map that maps the key type to the value type.
In this article, I will show how to implement a compile-time type map in C++ using C++ 17 features.
C++ Specialized Compile-Time Type Map
It’s possible to create specialized compile-time type maps using std::conditional_t and std::is_same_v or template specialization. But it is often inconvenient if there are many different type mappings in the map or there are many such maps to be defined.
In the following example, we used two different ways to define a specialized compile-time type map that maps the key type to the value type. More specifically, the type maps map int16_t to uint16_t, int32_t to uint32_t, and int64_t to uint64_t.
// Create a type map by specializing the type map. template <> structSpecializedTypeMap1<int16_t> { using find_type = uint16_t; };
template <> structSpecializedTypeMap1<int32_t> { using find_type = uint32_t; };
template <> structSpecializedTypeMap1<int64_t> { using find_type = uint64_t; };
intmain() { static_assert( std::is_same_v<SpecializedTypeMap0<int16_t>::find_type, uint16_t>); static_assert( std::is_same_v<SpecializedTypeMap0<int32_t>::find_type, uint32_t>); static_assert( std::is_same_v<SpecializedTypeMap0<int64_t>::find_type, uint64_t>); // Unable to compile because the key does not exist in the type map. // static_assert( // std::is_same_v<SpecializedTypeMap0<int8_t>::find_type, uint8_t>); static_assert( std::is_same_v<SpecializedTypeMap1<int16_t>::find_type, uint16_t>); static_assert( std::is_same_v<SpecializedTypeMap1<int32_t>::find_type, uint32_t>); static_assert( std::is_same_v<SpecializedTypeMap1<int64_t>::find_type, uint64_t>); // Unable to compile because the key does not exist in the type map. // static_assert( // std::is_same_v<SpecializedTypeMap1<int8_t>::find_type, uint8_t>); }
To build and run the program, please run the following commands.
We could see that if there are many different type mappings in the map, find_type will become very long and hard to maintain. In addition, the code in the SpecializedTypeMap0 and SpecializedTypeMap1 classes is not quite reusable.
C++ Generic Compile-Time Type Map
To create a generic compile-time type map, we created the following implementation. The generic compile-time type map can be used for creating multiple maps conveniently, similar to a std::map value map that is used at runtime.
template <typename KeyType, typename ValueType> structTypePair { using key = KeyType; using value = ValueType;
// Is does not matter what exactly the type is, as long as the type contains // the key type. The following also works. // static TypePair get_pair_instance(std::tuple<KeyType, KeyType>) static TypePair get_pair_instance(std::is_same<KeyType, KeyType>) { // This can always be constructed. return TypePair{}; } };
template <typename... TypePairs> structTypeMap : public TypePairs... { using TypePairs::get_pair_instance...;
static_assert(!has_duplicated_keys<TypePairs...>(), "Compile time type map got duplicate key values.");
template <typename QueryType> using find_type = typenamedecltype(get_pair_instance( std::is_same<QueryType, QueryType>{}))::value; };
// Define compile-time type maps. using TypePair0 = TypePair<int16_t, uint16_t>; using TypePair1 = TypePair<int32_t, uint32_t>; using TypePair2 = TypePair<int64_t, uint64_t>; using TypePair3 = TypePair<int64_t, uint64_t>;
using TypeMapIntToUInt0 = TypeMap<TypePair0>; using TypeMapIntToUInt1 = TypeMap<TypePair0, TypePair1>; using TypeMapIntToUInt2 = TypeMap<TypePair0, TypePair1, TypePair2>; // Unable to compile because of the has_duplicated_keys static_assert. // TypePair2 and TypePair3 have the same key type. // using TypeMapIntToUInt3 = TypeMap<TypePair0, TypePair1, TypePair2, // TypePair3>;
intmain() { static_assert( std::is_same_v<TypeMapIntToUInt0::find_type<int16_t>, uint16_t>); static_assert( std::is_same_v<TypeMapIntToUInt1::find_type<int16_t>, uint16_t>); static_assert( std::is_same_v<TypeMapIntToUInt1::find_type<int32_t>, uint32_t>); static_assert( std::is_same_v<TypeMapIntToUInt2::find_type<int16_t>, uint16_t>); static_assert( std::is_same_v<TypeMapIntToUInt2::find_type<int32_t>, uint32_t>); static_assert( std::is_same_v<TypeMapIntToUInt2::find_type<int64_t>, uint64_t>); // Unable to compile because the key does not exist in the type map. // static_assert( // std::is_same<TypeMapIntToUInt2::find_type<int8_t>, uint64_t>::value, // ""); }
To build and run the program, please run the following commands.