1
\$\begingroup\$

I am trying to save data about various entities in each Level in my game in a JSON file and load it at Runtime(Like a Scene file in Unity). I can save simple stuff like floats and ints and parse them back to with functions like std::stof. I can even handle stuff like a Vector3(3 floats).

Say, I want to save a User-type like CollisionSystem which resolves Collision between entities with a ColliderComponent. CollisionSystem does not contain any data and only Behaviour. How can I infer the Type from just the string and create an Object of CollisionSystem?

{
    "MovementSystem" : {},
    "RenderSystem" : {},
    "CollisionSystem" : {},
    "entities" : [
        {
            "PositionComponent" : {
                "x" : 10,
                "y" : 0,
                "z" : 10
            },
            "SpriteComponent" : {
                "source" : "\"assets/textures/Goblin.png\""
            },
            "ColliderComponent" : {}
        },
        {
            "PositionComponent" : {
                "x" : 0,
                "y" : 0,
                "z" : 30
            },
            "SpriteComponent" : {
                "source" : "\"assets/textures/Hero.png\""
            },
            "ColliderComponent" : {}
        }
    ]
}

The Brute Force method I could think of is a Switch case statement in the Parser like so:

System* GetSystem(string system_type)
{
    switch(system_type)
    {
        case "MovementSystem":
            return new MovementSystem();

        case "CollisionSystem"
            return new CollisionSystem();

        .
        .
        .

        default:
            return nullptr;
    }
}

which can be called like:

System* moveSystem = GetSystem("MovementSystem");
System* collisionSystem = GetSystem("CollisionSystem");

moveSystem->update();
collisionSystem->update();

However, I don't think it's the right way to do this. I am new to C++. So I might be missing some language features that might do this. Is there an elegant way to do this?

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Why do you want to save your systems to JSON? \$\endgroup\$
    – Vaillancourt
    May 25, 2019 at 14:09
  • 1
    \$\begingroup\$ As a side note, you may want to use a library (such as jsoncpp) instead of rewriting your own. \$\endgroup\$
    – Vaillancourt
    May 25, 2019 at 14:18
  • \$\begingroup\$ @AlexandreVaillancourt Because I need to know which systems are needed. I don't need a MovementSytem or CollisionSystem to be updated in the Main Menu Scene. \$\endgroup\$ May 25, 2019 at 15:17
  • \$\begingroup\$ I don't think there is much of a hurt if you have a system that is there and that updates 0 component. You could instantiate a system only if you encounter the component it uses. \$\endgroup\$
    – Vaillancourt
    May 25, 2019 at 18:04

1 Answer 1

0
\$\begingroup\$

Since C++ doesn't support reflection like C# it's not possible to do this in an automatic way. At the end of the day you will need some form of a jump table that converts the string to a type.

To automate this process as much as possible and be less error prone, a common strategy is to use macros to mark classes and a global hash map with lambdas that does the transformation for you.

class SystemRegister
{
    public:
        using CreationFunction = System*(*)(); // the signature of a function that takes no arguments and returns a System pointer.

        // Simple way to have a global singleton object
        static SystemRegister& Instance()
        {
            static SystemRegister sys_register;
            return sys_register;
        }

        // This needs to return a value so we can use it outside of the scope of a function. 
        // Without that the macro won't work.
        bool Register(const char* type_id, CreationFunction function)
        {
            m_creation_functions[type_id] = function;
            return true;
        }

        // This is the simplest form this method can have
        // Factory methods like this one should in return a std::unique_ptr (if you are using modern c++)
        // Additionally, you will probably have to add some asserts if the type_id doesn't exist in the map.
        System* Create(const char* type_id)
        {
            return m_creation_functions[type_id]();
        }

    private:
        // This map will do the transformation. Given a string it can return you a new object of the type.
        std::map<const char*, CreationFunction> m_creation_functions;
};
// With this macro you can register your systems in the map
// The bool is used as dummy value just to trick the compiler to allow this to be called outside of the scope of a function. (You can't call void functions on the global scope)
// registration##class_id will concatonate registration with the argument you pass to the macro ex. registrationCollisionSystem.
// #class_id will convert your argument to a c string. ex. "CollisionSystem"
// And finally the lambda will return a newly constructed class_id object as a System pointer.
#define REGISTER_CLASS(class_id) bool registration##class_id =\
                                 SystemRegister::Instance().Register(#class_id, []() -> System* { return new class_id; })

How to use:

.h
class CollisionSystem : public System
{
    ...
};

.cpp
REGISTER_CLASS(CollisionSystem);

void Foo()
{
    System* system = SystemRegister::Instance().Create("CollisionSystem");
}

```
\$\endgroup\$
3
  • \$\begingroup\$ Hey! This is exactly what I am trying to do, but I need Creation Functions that'll take Parameters for Constructors as well. So, I am also trying to generate these Creation Functions with a Separate JSON. This JSON contains metadata about the Class I am trying to Serialize/Deserialize like the Name, The Constructor Parameters and using these I am Generating the Parser/Creation Functions. Hopefully, that'll work out! \$\endgroup\$ Jun 9, 2019 at 7:44
  • \$\begingroup\$ You can change the signature of the CreationFunction to accept a JSON object and then do all the parsing inside the CreationFunction. \$\endgroup\$
    – Exaila
    Jun 9, 2019 at 11:14
  • \$\begingroup\$ Yup! Doing that! Coming from C# I miss Reflection very much! But thanks for your Help! \$\endgroup\$ Jun 9, 2019 at 16:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .