-1

I have a string variable which contains the name of the structure. This structure is declared in a header file. I would like to create an object of a structure based on the value of the structure name which is held in the string variable in C++.

struct myStruct{
    int a;
    char b;
};

string structName = "myStruct";
// Instantiate a structure variable [like this: "struct myStruct"]

Could anyone please help me with this?

Sridhar Lanka
  • 71
  • 1
  • 1
  • 6

2 Answers2

5

The feature you're looking for is called . This is something C++ has not. So you should fallback to, in this order:

  1. Question your design
  2. Write a hack

Here's an idea of a hack:

using result_type = /* some type, possibly void */;
std::unique_ptr<result_type> factory(std::string const& kind)
{
    if (kind == "alice") return new alice;
    if (kind == "bob")   return new bob;
    // ...
    return nullptr;
}
YSC
  • 38,212
  • 9
  • 96
  • 149
  • According to the types `alice` and `bob` really are, you should make them polymorphic or give the proper destructor to `std::unique_ptr`. – YSC Sep 12 '18 at 10:34
  • I have few structures which are already predefined in a header file. I have to use this header file and create an object of those structures based on which one's name is present in the string variable – Sridhar Lanka Sep 12 '18 at 10:36
  • *Introspection* wouldn't be sufficient to achieve unbounded creation of an object by class name determined at runtime. (Runtime) *reflection* is the name for a language feature that would be needed. – eerorika Sep 12 '18 at 10:40
  • @user2079303 IIRC reflection is introspection on itself, right? If yes, edit welcomed. – YSC Sep 12 '18 at 10:45
  • For emphasis: `1. Question your design`. The question probably indicates a wrong design. If there is a valid reason for it, there should be a proper, fleshed-out example. For instance, even if you could dynamically instantiate an object of some arbitrary type, how would that be useful if you could only default-initialise it? What would the subsequent code do with the instance? etc. If your answer is 'I would then check the string and execute different code paths accordingly', then (A) that may well be poor design again and (B) why don't you just do that for the instantiation, too? – underscore_d Sep 12 '18 at 10:48
  • @YSC as far as I know, introspection is the ability of a program to determine the type of an object at runtime. C++ has limited support for that in the form of RTTI (dynamic languages can go further inspect properties of an object as well). Reflection goes beyond and allows modifying the state of the program (i.e. set values of properties, create new instances etc.) – eerorika Sep 12 '18 at 10:51
0

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));
}
eerorika
  • 232,697
  • 12
  • 197
  • 326