It is not possible in C++ to create an instance of a class by name determined at runtime. C++ has very little ability to reflect.
You could however build the support for it yourself. The idea here is to create a map of name-string to a factory function which returns an instance of that type. The returned instance needs to be wrapped within std::any
because C++ - as a strongly and statically typed language - cannot let the return type to be determined at runtime.
There is a function add_factory
, which must be called for all types that you want to be able to instantiate using the name. There is also a helper macro, which like all macros, works because of magic.
auto& factories() {
static std::unordered_map<std::string, std::any(*)()> factories;
return factories;
}
template<class T>
void
add_factory(const char* name) {
// further development: take function as argument so that
// non-default-constructible classes can be supported
factories()[name] = []() -> std::any {
return T{};
};
}
std::any
create(const char* name)
{
const auto& f = factories();
if (f.find(name) != f.end())
return f.find(name)->second();
throw std::runtime_error("I haven't heard of this type");
}
// don't use this macro in header files
#define ADD_FACTORY(name) namespace { auto dummy_##name = (add_factory<name>(#name), 0); }
// ----- usage -----
struct a {
int i;
};
ADD_FACTORY(a)
struct b {
double d;
};
ADD_FACTORY(b)
// factories are not limited to classes
ADD_FACTORY(int)
int main()
{
std::any instance = create("a");
assert(std::any_cast<a>(&instance));
}