Reputation: 39
I want to implement some compile time evaluation involving taking all explicitly instantiated types of one specific template as input, just like this:
template<typename T>
struct Box{T content;};
struct ExplicitlyInstantiatedBoxTypesHolder {
using types = SOME_MAGICAL_META_PRGRAMMING_CODE; // todo
};
template<typename...>
void printTypes();
template<>
void printTypes(){}
template<typename First, typename... Rest>
void printTypes(){
cout << typeid(First).name() << ", ";
printTypeNames<Rest...>();
}
int main(){
Box<int> box1;
Box<long> box2;
printTypes<ExplicitlyInstantiatedBoxTypesHolder::types...>();
// expected output:
// Box<int>, Box<long>
}
Upvotes: 1
Views: 136
Reputation: 13752
You can register the types that are created into a static 'Regsitrar'.
Let's have a base for all Box types. This base would also manage the registration and printing:
class BoxBase {
private:
static std::set<std::string>& types() {
static std::set<std::string> types;
return types;
}
protected:
template<typename T>
static int registerType() {
std::string name = boost::typeindex::type_id<T>().pretty_name();
if(types().insert(name).second) {
// for the need of the line below see:
// https://stackoverflow.com/questions/59365123
static std::ios_base::Init force_init;
std::cout << "registering a box of type: " << name << std::endl;
}
return 0;
}
public:
static void print_types() {
for(const auto& type: types()) {
std::cout << type << ' ';
}
std::cout << std::endl;
}
};
Now class Box has to make sure it registers itself per each type created:
template<typename T> class Box: public BoxBase {
const static int foo_;
public:
Box() {
std::cout << "creating a box of type: ";
std::cout << boost::typeindex::type_id<T>().pretty_name() << std::endl;
}
~Box() {
// we use foo_ here to avoid optimizing its initialization
std::cout << foo_ << std::endl;
}
void doSomething() const {}
};
template<typename T>
const int Box<T>::foo_ = BoxBase::registerType<T>();
Now all Box types would be recorded, regardless of the location they are created!
int main() {
std::cout << "-------------------------------" << std::endl;
std::cout << "box types: ";
BoxBase::print_types();
std::cout << "-------------------------------" << std::endl;
Box<long> long_box;
Box<int> int_box;
long_box.doSomething();
int_box.doSomething();
Box<long> long_box2;
Box<double> double_box;
}
void bar() {
Box<char> box;
}
Output:
registering a box of type: long
registering a box of type: int
registering a box of type: double
registering a box of type: char
-------------------------------
box types: char double int long
-------------------------------
creating a box of type: long
creating a box of type: int
creating a box of type: long
creating a box of type: double
Code: http://coliru.stacked-crooked.com/a/c69162c8da91e51e
You can allow the user of the template to declare ahead the types that may be used, then only these types would be allowed and you can print the list of these types. This is also not exactly what you look for but maybe can be of a help to the problem in question.
The idea is to have a factory template that would manage the allowed types.
template<typename... Ts>
struct BoxesFactory {
template<typename T>
static Box<T> create_box() {
static_assert(TypeExists<T, Ts...>::value);
return Box<T>{};
}
static constexpr void print_types() {
TypesPrinter<Ts...>();
}
};
int main() {
BoxesFactory<int, long> factory;
std::cout << "Supported boxes: " << std::endl;
factory.print_types();
auto long_box = factory.create_box<long>();
auto int_box = factory.create_box<int>();
// auto double_box = factory.create_box<double>(); // <= compilation error
long_box.do_box_things();
int_box.do_box_things();
}
The class Box itself can be declared inside the private part of BoxFactory if you want to avoid the creation of Boxes outside the factory, or alternatively you can require in the ctor of Box a private token parameter that only BoxFactory can pass.
template<typename Validate, typename T, typename... Ts>
struct TypeExists {
constexpr static bool value = TypeExists<Validate, T>::value
|| TypeExists<Validate, Ts...>::value;
};
template<typename Validate, typename T>
struct TypeExists<Validate, T> {
constexpr static bool value = std::is_same<Validate, T>::value;
};
template<typename T, typename... Ts>
struct TypesPrinter {
constexpr TypesPrinter() {
TypesPrinter<T>();
TypesPrinter<Ts...>();
}
};
template<typename T>
struct TypesPrinter<T> {
constexpr TypesPrinter() {
// std::cout << typeid(T).name() << ' ';
std::cout << boost::typeindex::type_id<T>().pretty_name() << ' ';
}
};
Code: http://coliru.stacked-crooked.com/a/42f67cc6ce95e5c5
Side note: there are other techniques for restricting the instantiation of a template to certain types. But those do not deal with 'bookeeping' of the types being actually in use.
Upvotes: 1