1

I have a school project in which there is a world simulation. Teacher wants me to do save/load system and I've encountered a problem. My data is saved in a format name x y so saving works fine.

Problem starts when I want to load data. This is my solution:

switch(name) {
case "Human":
    new Human(x,y);
    break;
case "Dog":
    new Dog(x,y);
    break;
}

Is there a way to generalize this? Saved name is always exactly the same as constructor name, so I would just like to do something like:

string name = "Human"
new <name>(x,y)   <->   new Human(x,y);

My solution works just fine but following the rules of OOP, the world shouldn't know what kind of organisms live on it.

4
  • The code you have provided is probably not doing what you want it to. You are allocating and constructing an object but have to reference/pointer to that memory (leaking memory). Commented May 12, 2020 at 16:33
  • Guys - I know that my example code doesn't do anything there. It's just an example what I want to achieve Commented May 12, 2020 at 16:37
  • I have an Organizm class, then Plants and Animals which derive from Organism and then classes like Dog, Human, Flower Commented May 12, 2020 at 16:37
  • I just wanted to know if it is possible to construct an object from variable so for a given variable string name = "Human" code will execute new Human without using switch or if conditions Commented May 12, 2020 at 16:38

4 Answers 4

2

No, currently there isn't. C++ doesn't have reflection and introspection which is required for something like this to work. (There is active work being done in this direction, but don't expect it to come into standard very soon).

There are serialization libraries which will hide the equivalent of your intended switch and provide a simpler, safer API and they are the preferred way to do this in production, but for your assignment you should do it manually.

By the way, your code is syntactically incorrect, it shouldn't compile, but I guess I get what you meant.

Sign up to request clarification or add additional context in comments.

3 Comments

I am aware of the fact that my code won't compile. It has been written just as an example to explain the problem.
@cigien not, not at all. Templates are a compile time feature and you know the type of your object at runtime, on reading from file.
Oh of course, that was silly. I didn't read the question closely enough.
2

You can simplify the process of string comparison using macros. But you still have to provide a list of classes that need to be searched.

#define CHECK_RETURN(name, className) if (name == #className) return new className();

std::string name = "Dog";

CHECK_RETURN(name, Human);
CHECK_RETURN(name, Dog);
CHECK_RETURN(name, Banana);

5 Comments

No, that requires reflection - as some of the other answers pointed out. But you can kinda fake reflection if you really want to by having a base class which contains a map of function pointers and each child class is responsible for registering its function pointer. Let me know if you want me to update the answer with this approach and an example.
Ok, I saw that someone already wrote the same answer so nevermind.
This project ain't that complex to use such an advanced method. I've just thought that there's a fairly simple solution but seems that there's not.
I just thought of another solution that doesn't require function pointers, but requires writing a separate console application that parses all your header files and finds all class declarations and dynamically writes the definition of a create function into a source file. If you run this console application in your prebuild event, you don't have to think about it and just use a forward declaration of said create function everywhere else in your code. But I guess this is even more complicated than using function pointers.
@RanjeethMahankali That's usually done in the other direction by writing a description of the objects in another small language (IDL) then generating the header file class definitions. That way the generator can produce outputs for C++, Java, C#, Go, Rust, Python, etc.
2

No. Not in C++. To do that you would need reflection, and that is not a thing C or C++ can do.

What is done in some cases is to write an Interface Definition Language, aka IDL, and from that generate code that implements the interface. These interfaces often include the ability to serialize and deserialize objects in order to send them across the network, but it works for files as well.

That's probably more than you want to get into.

What you want for a simple C++ project is to implement a Factory. I assume all these things are Organisms so you want an OrganismFactory like:

class OrganismFactory {
public:
    static std::unique_ptr<Organism> Create(const std::string& line);
};

And then it reads the contents of a line and produces an Organism. Probably using something like your case statements. Or you can create a std::map or std::unordered_map of the class name and a function pointer to the rest of the line. Then there's no if or case for each object type, just a map lookup and an indirect function call. You still have to write the code to fill in the map though, and write each function.

And yes by OOP rules you need to create interfaces/virtual methods in the Organism base class for everything that Organisms do in the world.

Comments

1

You can create your own lookup table of creator functions to handle this, for example:

class Organism
{
public:
    virtual ~Organism() {}
};

class Human : public Organism
{
...
};

class Dog : public Organism
{
...
};

...

using OrganismPtr = std::unique_ptr<Organism>;
using CreateFunc = OrganismPtr(*)(int, int);
std::map<std::string, CreateFunc> mymap;
mymap["Human"] = [](int x, int y) -> OrganismPtr { return new Human(x, y); }
mymap["Dog"] = [](int x, int y) -> OrganismPtr { return new Dog(x, y); }
...

string name = "Human";
OrganismPtr o = mymap[name](x, y);
// use o as needed...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.