The typeid
allows to assign a unique std::type_index
to each type at runtime. I would like to do the same, statically using two metafunctions :
// Get a unique integral number associated with the provided type
template <class T>
struct encode_type
{
using type = T;
static constexpr std::size_t value = /* Metaprogramming magic */;
};
// Get the type uniquely associated with the provided value
template <std::size_t V>
struct decode_type
{
static constexpr std::size_t value = V;
using type = /* Metaprogramming magic */;
};
Is there a way to do that in C++11?
Here's a possible solution which "works" with GCC 5.2 and Clang 3.7.
I use Filip Roséen's Constexpr Meta-Container with some slight alterations. As T.C. pointed out, this may be made ill-formed in the future, so this solution is totally unreasonable in production code, but it's pretty cool for now. I'm not even sure if this is 100% standards-conformant.
// This is our meta-container
using TypeMap = atch::meta_list<class A>;
// Get a unique integral number associated with the provided type
template <class T>
struct encode_type
{
using type = T;
// Push T into the container and store the pre-push size
//( requires slight change to Filip's code)
static constexpr std::size_t value = TypeMap::push<T>();
};
// Get the type uniquely associated with the provided value
template <std::size_t V>
struct decode_type
{
static constexpr std::size_t value = V;
// Get the type at index V
// (requires a small helper function addition)
using type = decltype(TypeMap::at<V>());
};
The alterations I made to the original code:
template<class T, class H = meta_list, std::size_t Size = counter::value()>
static constexpr std::size_t push (
size_type = push_state<
typename H::template value<>::template push<T>::result
> ()
) { return Size; }
I modified atch::meta_list::push
to return the size of the meta-container before the push. I used a template parameter with a default argument to ensure that the size is calculated before the push.
template<size_type Idx, class H = meta_list>
static constexpr auto at () -> typename H::template value<>::template at<Idx>::result;
I added a small decltype
helper function in atch::meta_list
in order to hide all that dependent name mess.
Some test code:
int main () {
std::array<int, 4> encoded {
encode_type<int>::value,
encode_type<double>::value,
encode_type<std::string>::value,
encode_type<float>::value
};
std::cout << "Encoding: ";
for (auto i : encoded) std::cout << i << ", ";
std::cout << std::endl;
std::array<std::type_index, 4> decoded {
typeid(decode_type<0>::type),
typeid(decode_type<1>::type),
typeid(decode_type<2>::type),
typeid(decode_type<3>::type),
};
std::cout << "Decoding: ";
for (auto i : decoded) std::cout << i.name() << ", ";
std::cout << std::endl;
}
Both Clang and GCC spew a bunch of warnings, but they both "work"!
Clang compiles, runs and outputs:
Encoding: 0, 1, 2, 3,
Decoding: i, d, NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, f,
GCC compiles, runs and outputs:
Encoding: 0, 1, 2, 3,
Decoding: i, d, NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, f,
Maybe you would be better with a pre-processing step...
There are more than types in C++ than can be counted by size_t
.
As proof:
template<size_t N> struct bob {};
there are exactly as many bob<X>
types as there are size_t
values.
template<class...> struct alice {};
now, alice<bob<1>, bob<2>>
is a valid type. So is alice<alice<bob<1>>
.
There are many more alice
types than there are bob
types.
Thus there is no injection from the set of size_t
values to the set of types.
Thus any such mapping must be incomplete if bijective. You can only map some subset of types to size_t
and back again.
You did not specify you only wanted to handle a subset of types, so the answer is, you cannot do what you asked.
Note that std::type_index
is simply a hashable, comparable object. It is not an "index" like an integer value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With