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
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);
}
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.
Related
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.
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.
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);
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.
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...
I have a base class named shapes with derived class of a 3D shape, i.e. Ball or Tetraeder.
My program should read the type of the shape and it's parameters from a text file and write the volume and area to an output text.
#include <fstream>
#include <string>
#include <sstream>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include "Shape.h"
#include "Ball.h"
#include <vector>
using namespace std;
int
main( int argc, char** argv )
{
string input = string(argv[1]);
string output = string(argv[2]);
ifstream file(input);
string line;
string shapename;
int nx = atoi(argv[3]);
int ny = atoi(argv[4]);
int nz = atoi(argv[5]);
while (std::getline(file, line))
{
std::stringstream lineStream(line);
lineStream >> shapename;
int value;
std::vector<int> lineData;
while (lineStream >> value)
{
lineData.push_back(value);
}
Shape * objShape = new shapename(lineData);
objShape -> calc_volume;
objShape -> calc_projection(nx,ny,nz);
std::ofstream f(output);
f << objShape -> get_volume() << " " << objShape -> get_projection << endl;
}
}
My Question is now how can i create an object from a string in a textfile, especially without knowing all derived classes.
It should be possible to add more shapes to the program without changing the code, only by adding new files.
The question is:
My Question is now how can i create an object from a string in a
textfile, especially without knowing all derived classes.
The answer is: you have to know all the derived classes.
C++ does not have reflection. As such all class names are bound at compile time, and this kind of a factory has no choice but to do some variation of:
if (name == "box")
return new Box();
else if (name == "circle")
return new Circle();
// ... etc ... etc ...
There are various different approaches and design patterns that make it possible to automate some of this drudge work, and make this flexible enough to avoid having to explicitly maintain a hardcoded list of all subclasses.
I'll just outline a brief, very brief approach. A very simple one that I've used before, and achieves, pretty much, the same result: a factory that can instantiate a given subclass by name, in a manner where you don't have to manually edit the factory, and add a few more lines of code. The entire process of creating a factory for a new subclass can be neatly wrapped into the process of creating a new subclass, making this a fairly bulletproof, compartmentalized solution.
Consider a simple mechanism for registering a factory for these subclasses:
typedef Shape (*shape_factory_t)();
Shape is your superclass of shapes.
The factory would work something like this:
std::map<std::string, shape_factory_t> all_factories;
void register_factory(const std::string &name, shape_factory_t factory)
{
all_factories[name]=factory;
}
So now you have a map of all your factories. Instead of an endless if statement you have a single map, which you can look up by class name, and call the appropriate factory, something like:
auto iter=all_factories.find(name);
if (iter == all_factories.end())
throw; // Some exception, unknown subclass
return (*iter->second)();
All right, that part's taken care of. The issue now becomes: how to register a factory for each subclass.
Let's say you have an implementation of Circle:
class Circle : public Shape {
class initializer;
// ... other things that make up the Circle
};
Then, in circle.cpp, which implements this subclass:
static Shape *create_circle()
{
return new Circle(); // Add constructor parameters, as appropriate
}
class Circle::initializer {
public:
initializer() {
register_factory("circle", create_circle);
}
};
static initializer initialize_me;
In this manner, the Circle class registers itself with the factory that creates an instance of a given Shape, by class name. You can proceed and implement all other subclasses, individually, without touching the main factory code. You can declare your Box subclass in the same manner, and have it register itself with the factory, which will then automatically know to create a Box class (presumably by invoking the create_box() function), given the name "box".
There is one other detail that needs to be taken care of: initialization order. As you know, the relative initialization order of globally-scoped objects in different translation units is implementation defined, and is otherwise unspecified by C++.
The global std::map of all factory functions must be constructed before all the subclasses try to register themselves, and put themselves into the map, when the application starts.
This is a fairly typical static initialization order fiasco question, for which there are several known solutions. The one explained in this answer should work fine, here.
C++ isn't so flexible. Adding new shapes would mean adding new classes (since you have already made a Shapes, a Ball and a Tetraeder class, I'm assuming you want to make more classes). And if you add new classes, you'll have to change the code, which means you have to recompile.
You have to know what the derived classes are. You're the one who codes them, so you might as well also have a list of them. The best thing you can do about your program being flexible is using header files, which you already seem to be doing anyway.
As for creating an object from a string in a text file (while you know what the 3D object classes are), you can parse the string, read what kind of a shape it wants to make and then do something fairly simple such as this:
//shapeType - a string containing the type of the 3D object
Shape *newShape;
switch(shapeType) {
case "ball":
newShape = new Ball(...); // ... - parameters for the ball dimensions
break;
case "tetraeder":
newShape = new Tetraeder(...); // ... - parameters again
break;
default:
return -1;
}
//and now you can use newShape as you wish
Class World is designed to spawn Actors.
Actors can be different types: it's can be Cat, can be Dog, can be Tree, everything, the only similar thing is that they all derived from Actor.
And there is also command line, it gives World a string where written next things:
what_to_do string_leftover. There if what_to_do equals "Spawn" then first word of string_leftover must be type name, second - Actor name.
The problem is that number of Actors can be indefinite - I don't really sure about their count and I'am really scared of forget to write their Spawn overload manually.
If very simple:
I entered in console: "SelectWorld World1" then "Spawn PhysicalPendulum OneMorePendulum". This will select world named World1 (this is working perfectly) and spawn Actor of type PhysicalPendulum with name OneMorePendulum.
Problem: I can't elegantly determine what type I should spawn from string.
Why:
All solutions I know requires to create "string to type determiners" not in actor-derived class header/object file.
I mean I can use switch (pointer-to-function map), but each time I create new Actor type I need to return to that Spawn function and write down new type to spawnables, there is a slight chance that I can totally forget about it and looooooong time of debugging will be awaiting me.
I can create static class instance with macro, but there is no guarantee that Spawner will be created before this instance initializes.
I thought about tricks with macros, but they can't be extended, like if certain define had "definition_body" I can't add "another_definition_body" to it and get "definition_body another_definition_body".
How can I elegantly spawn type from string?
Declare a global function/macros that will 'register' newly create class to the list of known e.g.
//SpawnActor.h
#pragma once
using std::string;
using std::map;
using std::function;
class Actor;
class ActorSpawner
{
public:
typedef function<Actor()> SpawnFunction;
static void RegisterActorClass(const string& name, const SpawnFunction& function)
{
s_spawnClasses[name] = function;
}
void SpawnClass(const string& name)
{
// You'll need to validate name
Actor* a = s_spawnClasses[name]();
//...
}
private:
static map<string, function<Actor()> s_spawnClasses
};
//SpawnActor.cpp
#include "SpawnActor.h"
map<string, function<Actor()> SpawnFunction::s_spawnClasses();
//TreeActor.h
class TreeActor : public Actor
{
...
};
//TreeActor.cpp
#include "TreeActor.h"
ActorSpawner::RegisterActorClass("TreeActor", [](){return new TreeActor();})
For your code to create an object, you must know the type of the object you are creating. At some point you must say thing* = new Thing()
You can have a factory that knows all the types of objects allowed to be created. If you use a standardised naming convention you can use token pasting. thing* = new Thing ## string_name. i.e. string = "Robot". #define ABC(name) return new Thing ## name. class ThingRobot {}; etc...
I was recently in a job interview and my interviewer gave me a modeling question that involved serialization of different shapes into a file.
The task was to implements shapes like circle or rectangles by first defining an abstract class named Shape and then implements the various shapes (circle, rectangle..) by inheriting from the base class (Shape).
The two abstract methods for each shape were: read_to_file (which was supposed to read the shape from a file) and write_to_file which supposed to write the shape into a file.
All was done by the implementation of that virtual function in the inherited shape (Example: For Circle I was writing the radius, for square I saved the side of the square....).
class Shape {
public:
string Shape_type;
virtual void write_into_file()=0;
virtual void read_into_files()=0;
Shape() {
}
virtual ~Shape() {
}};
class Square: public Shape {
public:
int size;
Square(int size) {
this->size = size;
}
void write_into_file() {
//write this Square into a file
}
void read_into_files() {
//read this Square into a file
}
};
That was done in order to see if I know polymorphism.
But, then I was asked to implement two functions that take a vector of *shape and write/read it into a file.
The writing part was easy and goes something like that:
for (Shape sh : Shapes) {
s.write_into_file();
}
as for the reading part I thought about reading the first word in the text (I implemented the serializable file like a text file that have this line: Shape_type: Circle, Radius: 12; Shape_type:Square...., so the first words said the shape type). and saving it to a string such as:
string shape_type;
shape_type="Circle";
Then I needed to create a new instance of that specific shape and I thought about something like a big switch
<pre><code>
switch(shape_type):
{
case Circle: return new circle;
case Square: return new square
......
}
</pre></code>
And then, the interviewer told me that there is a problem with this implementation
which I thought was the fact that every new shape the we will add in the future we should also update int that big swicht. he try to direct me into a design pattern, I told him that maybe the factory design pattern will help but I couldn't find a way to get rid of that switch. even if I will move the switch from the function into a FactoryClass I will still have to use the switch in order to check the type of the shape (according to the string content i got from the text file).
I had a string that I read from the file, that say the current type of the shape. I wanted to do something like:
string shape_type;
shape_type="Circle";
Shape s = new shape_type; //which will be like: Shape s = new Circle
But I can't do it in c++.
Any idea on what I should have done?
In you factory you could map a std::string to a function<Shape*()>. At startup you register factory methods will the factory:
shapeFactory.add("circle", []{new Circle;});
shapeFactory.add("square", []{new Square;});
shapeFactory.add("triangle", []{new Triangle;});
In your deserialization code you read the name of the type and get its factory method from the factory:
std::string className = // read string from serialization stream
auto factory = shapeFactory.get(className);
Shape *shape = factory();
You've now got a pointer to the concrete shape instance which can be used to deserialize the object.
EDIT: Added more code as requested:
class ShapeFactory
{
private:
std::map<std::string, std::function<Shape*()> > m_Functions;
public:
void add(const std::string &name, std::function<Share*()> creator)
{
m_Functions.insert(name, creator)
}
std::function<Shape*()> get(const std::string &name) const
{
return m_Functions.at(name);
}
};
NOTE: I've left out error checking.
In C++, with
for (Shape sh : Shapes) {
s.write_into_file();
}
you have object slicing. The object sh is a Shape and nothing else, it looses all inheritance information.
You either need to store references (not possible to store in a standard collection) or pointers, and use that when looping.
In C++ you would to read and write some kind of type tag into the file to remember the concrete type.
A virtual method like ShapeType get_type_tag() would do it, where the return type is an enumeration corresponding to one of the concrete classes.
Thinking about it, though, the question was probably just getting at wanting you to add read and write functions to the interface.
You could create a dictionary of factory functions keyed by a shape name or shape id (shape_type).
// prefer std::shared_ptr or std::unique_ptr of course
std::map<std::string, std::function<Shape *()>> Shape_Factory_Map;
// some kind of type registration is now needed
// to build the map of functions
RegisterShape(std::string, std::function<Shape *()>);
// or some kind of
BuildShapeFactoryMap();
// then instead of your switch you would simply
//call the appropriate function in the map
Shape * myShape = Shape_Factory_Map[shape_type]();
In this case though you still have to update the creation of the map with any new shapes you come up with later, so I can't say for sure that it buys you all that much.
All the answers so far still appear to have to use a switch or map somewhere to know which class to use to create the different types of shapes. If you need to add another type, you would have to modify the code and recompile.
Perhaps using the Chain of Responsibility Pattern is a better approach. This way you can dynamically add new creation techniques or add them at compile time without modifying any already existing code:
Your chain will keep a linked list of all the creation types and will traverse the list until it finds the instance that can make the specified type.
class Creator{
Creator*next; // 1. "next" pointer in the base class
public:
Creator()
{
next = 0;
}
void setNext(Creator*n)
{
next = n;
}
void add(Creator*n)
{
if (next)
next->add(n);
else
next = n;
}
// 2. The "chain" method in the Creator class always delegates to the next obj
virtual Shape handle(string type)
{
next->handle(i);
}
);
Each subclass of Creator will check if it can make the type and return it if it can, or delegate to the next in the chain.
I did create a Factory in C++ some time ago in which a class automatically registers itself at compile time when it extends a given template.
Available here: https://gist.github.com/sacko87/3359911.
I am not too sure how people react to links outside of SO but it is a couple of files worth. However once the work is done, using the example within that link, all that you need to do to have a new object included into the factory would be to extend the BaseImpl class and have a static string "Name" field (see main.cpp). The template then registers the string and type into the map automatically. Allowing you to call:
Base *base = BaseFactory::Create("Circle");
You can of course replace Base for Shape.
This question already has answers here:
Is there a way to instantiate objects from a string holding their class name?
(12 answers)
Closed 8 years ago.
I'm working on a game and am trying to implement a smart way to create npc-objects in C++ from parsing a text file.
Currently this is hard coded in a Factory-object. Like this:
IActor * ActorFactory::create(string actortype, Room * r, string name, int hp)
{
if(actortype == "Troll")
{
return new Troll(r, name, hp);
}
if (actortype == "Dragon")
{
return new Dragon(r, name, hp);
}
// ... and so on
throw "Can't recognize type '"+actortype+"'.";
}
This is in my opinion a very ugly way to do it. Since it (among other things) breaks the Open/Closed principle.
I am schooled in Java, and in Java I would do something like having each IActor report it's class name and class type to the ActorFactory in the beginning of program execution. The factory would then store the relation in a map and can then easily look up what string maps to which object and it can then easily instantiate it.
Edit: I would also like to have the ability to call the constructor with a variable number/type of arguments.
How would this be done in C++? Can it be done?
In C++, you would typically use the Abstract Factory design pattern.
The point is: "the decision about the type of actor to create should not be the responsibility of ActorFactory::create()." In your case, this method should not decide which class to instantiate based on a string but would rather rely on a type; this type is the actual factory class.
Each actor class has its own factory class: TrollFactory, DragonFactory, etc. deriving from a base class ActorFactory2 (trailing 2 because ActoryFactory is already taken);
Each specialized factory class implements a virtual create() method without parameter returning a pointer to a newly created actor class;
If you need parameters to construct an actor, pass them to the factory object before creating the actor: pass them in the ctor and store them as member variables; create() will retrieve them later upon creation of the actor;
This way, you can easily pass different arguments for different actors and your factory mechanism will be scalable (a step toward the Open/Closed principle);
Now, ActorFactory::create() accepts a pointer to an object deriving from ActorFactory2 and calls the ActorFactory2::create() method: it will create the requested actor with appropriate arguments without switch statement.
class ActorFactory2
{
string m_name; // Each IA actor has a name
int m_hp; // and some HP
public:
ActorFactory2( const string &p_name, int p_hp )
: m_name( p_name ), m_hp( p_hp ) {}
virtual IActor * create() const = 0;
};
class TrollFactory : public ActorFactory2
{
// No special argument needed for Troll
public:
TrollFactory( const string &p_name, int p_hp )
: ActorFactory2( p_name, p_hp ) {}
virtual IActor * create() const { return new Troll( m_name, m_hp ); }
};
class DragonFactory : public ActorFactory2
{
FlameType m_flame; // Need to indicate type of flame a dragon spits
public:
DragonFactory( const string &p_name, int p_hp, const FlameType &p_flame )
: ActorFactory2( p_name, p_hp )
, m_flame( p_flame ) {}
virtual IActor * create() const { return new Dragon( m_name, m_hp, m_flame ); }
};
IActor * ActorFactory::create( const ActorFactory2 *factory )
{
return factory->create();
}
int main( int, char ** )
{
ActorFactory af;
...
// Create a dragon with a factory class instead of a string
ActorFactory2 *dragonFactory = new DragonFactory( "Fred", 100, RedFlames );
IActor *actor = af.create( dragonFactory ); // Instead of af.create( "dragon", ... )
delete dragonFactory;
}
I have answered in another SO question about C++ factories. Please see there if a flexible factory is of interest. I try to describe an old way from ET++ to use macros which has worked great for me.
ET++ was a project to port old MacApp to C++ and X11. In the effort of it Eric Gamma etc started to think about Design Patterns
The specific term is: parameterized factory method and it is part of the factory method design pattern.
To use a generic factory, hold the classes in a map and access via a string. If your class names are usable, register the class to the factory with "typeid(MyClass).name() and return a copy of the class by providing a clone() member function.
However, for simple not to extensible factories, I use the approach from your question.
I can't answer your question about passing more variable parameters, but to deserialize, it is enough to pass the portion to the class and let it deserialize itself (as you already seem to do).
You could use a map to store function pointers that'd return Actor*, with that being a pointer to the object being created. so then the code would just be
std::map<std::string,IActor* (*) (Room*,std::string,int)> constructorMap
constructorMap["Troll"]=&TrollConstructor
//etc...
IACtor* ActorFactory::create(string actortype,Room* r,string name,int hp){
return (*constructorMap[actortype])(r,name,hp);
}
(please excuse any possible screw-ups I made with the function pointers, they are not my strong point)