I'm struggling with some of the rules of what can be pushed into compile time calculations. Here I've written code that associates a unique ID with each class that requests one (and a demangled name for testing purposes.) However, this unique ID can't be used as a template argument or a part of a static_assert condition, because it isn't a constexpr.
#include <cassert>
#include <cxxabi.h>
#include <iostream>
#include <typeinfo>
namespace UID {
static int nextID(void) {
static int stored = 0;
return stored++;
}
template<class C>
static int getID(void) {
static int once = nextID();
return once;
}
template<class C>
static const char *getName(void) {
static int status = -4;
static const char *output =
abi::__cxa_demangle(typeid(C).name(), 0, 0, &status);
return output;
}
}
namespace Print {
template<class C>
std::ostream& all(std::ostream& out) {
return out << "[" << UID::getID<C>() << "] = "
<< UID::getName<C>() << std::endl;
}
template<class C0, class C1, class... C_N>
std::ostream& all(std::ostream& out) {
return all<C1, C_N>(all<C0>(out));
}
}
void test(void) {
Print::all<int, char, const char*>(std::cout) << std::endl;
// [0] = int
// [1] = char
// [2] = char const*
Print::all<char, int, const char*>(std::cout);
// [1] = char
// [0] = int
// [2] = char const*
}
If it isn't clear, I'd like to change other compile-time behavior based on the ID. I've seen several approaches that involved a linked list of types, so that the ID is the sum of a previously assigned constexpr ID and a constexpr offset. However, I don't see how this is an improvement over manually assigning ID's. If you were to sort one list of classes by their ID's, then wrap each of the classes and request ID's for the wrappers, the ID's would depend on the sorting; then to determine the "last" element, you would have to either sort the elements manually! What am I missing?