# How to parse User-defined types from String?

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?

• Why do you want to save your systems to JSON? – Vaillancourt May 25 '19 at 14:09
• As a side note, you may want to use a library (such as jsoncpp) instead of rewriting your own. – Vaillancourt May 25 '19 at 14:18
• @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. – Vignesh Gunasekaran May 25 '19 at 15:17
• 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. – Vaillancourt May 25 '19 at 18:04

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");
}

$$$$
`
• 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! – Vignesh Gunasekaran Jun 9 '19 at 7:44
• You can change the signature of the CreationFunction to accept a JSON object and then do all the parsing inside the CreationFunction. – Exaila Jun 9 '19 at 11:14
• Yup! Doing that! Coming from C# I miss Reflection very much! But thanks for your Help! – Vignesh Gunasekaran Jun 9 '19 at 16:41