0

Background:

In my game engine I have a generic 'script parser' which is used to create game entities by parsing a script file. So in code you would have something like MyEntity* entity = MyScriptParer::Parse("filename.scr");

Any class which is to be scriptable inherits from a generic base class. Internally in the game engine there are some specific classes that use this - particles, fonts etc and this all works nicely in the parser - see extract below

std::string line;
std::getline(ifs, line);

if (line == "[FONT]") {
    CFont* f = new CFont();
    f->readObject(ifs);
}
else if (line == "[PARTICLE]") {
    CParticle* p = new CParticle();
    p->readObject(ifs);
}
...

My problem comes with how to handle user defined classes i.e classes in the games that use the game engine. The base class has an abstract method readObject so anything which inherits must implement this method.

The issue is how would the parser know about the new class? E.g say I have a CVehicle class the parser would now need to know to recognise "[VEHICLE]" and also be able to create a new CVehicle

Is there any way to store a class type or something in an array/map so maybe I could have a function to register a list of class types with strings to provide a lookup for creating the new instances?

Bit of a long shot and may not be possible so if anyone has other suggestions on how to approach the parsing they will be welcomed

2 Answers 2

1

You can store a class type in an array/map via std::type_info

However, you cannot create a type from this, as it would require more RTTI than is available in C++. (like reflection in .NET).

However, you could store a function pointer to a class factory in such a map. I.e.

typedef CBaseClass* (*pfnCreateClass)();

std::map<std::string, pfnCreateClass> mapCreate;

// Registering
// CMyCustomClass::GetClass() is a static method that creates a CMyCustomClass
mapCreate.insert(std::pair<std::string, pfnCreateClass>("[CUSTOM_CLASS]", CMyCustomClass::GetClass));

// Get class
std::map<std::string, pfnCreateClass>::const_iterator it = mapCreate.find(line);
if(mapCreate.end() != it)
{
    CBaseClass *p = it->second();
    p->readObject(ifs);
}
Sign up to request clarification or add additional context in comments.

3 Comments

Hi, thanks for the informative answer. I will give this a try and see how it goes.
OK I like this approach as it means I can make the complete script reader a bit more generic and have even internal scriptables added as new ones get added to the game engine. I have tried to implement and come across one issue though. It is with the CMyCustomClass::GetClass() part. If I try to pass it as per your example the compiler fails with "Cannot initialize a parameter of type 'CBaseClass ()()' with an lvalue of type 'CFont *()': different return type ('CBaseClass *' vs 'CFont *')"
I fixed my issue. Was a simple error of me passing back a pointer to the new customer class, CFont in this case, instead of passing it back a the base class pointer
0

Just have the function to register a new type take in the name of the type and a function for creating the type.

Something like so:

void RegisterType( std::string name, std::function< BaseType() > createFunc );

When registering a new type you do it like so:

RegisterType( "Vehicle", [](){ return new CVehicle; } );

That way the parser can create all the derived types.

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.