I came across an open source C++ code and I got curious, why do people design the classes this way?
So first things first, here is the Abstract class:
class BaseMapServer
{
public:
virtual ~BaseMapServer(){}
virtual void LoadMapInfoFromFile(const std::string &file_name) = 0;
virtual void LoadMapFromFile(const std::string &map_name) = 0;
virtual void PublishMap() = 0;
virtual void SetMap() = 0;
virtual void ConnectROS() = 0;
};
Nothing special here and having an abstract class can have several well understood reasons. So from this point, I thought maybe author wanted to share common features among other classes. So here is the next class, which is a seperate class but actually holds a pointer of type abstract class mentioned above (actual cpp file, other two classes are header files) :
class MapFactory
{
BaseMapServer *CreateMap(
const std::string &map_type,
rclcpp::Node::SharedPtr node, const std::string &file_name)
{
if (map_type == "occupancy") return new OccGridServer(node, file_name);
else
{
RCLCPP_ERROR(node->get_logger(), "map_factory.cpp 15: Cannot load map %s of type %s", file_name.c_str(), map_type.c_str());
throw std::runtime_error("Map type not supported")
}
}
};
And now the interesting thing comes, here is the child class of the abstract class:
class OccGridServer : public BaseMapServer
{
public:
explicit OccGridServer(rclcpp::Node::SharedPtr node) : node_(node) {}
OccGridServer(rclcpp::Node::SharedPtr node, std::string file_name);
OccGridServer(){}
~OccGridServer(){}
virtual void LoadMapInfoFromFile(const std::string &file_name);
virtual void LoadMapFromFile(const std::string &map_name);
virtual void PublishMap();
virtual void SetMap();
virtual void ConnectROS();
protected:
enum MapMode { TRINARY, SCALE, RAW };
// Info got from the YAML file
double origin_[3];
int negate_;
double occ_th_;
double free_th_;
double res_;
MapMode mode_ = TRINARY;
std::string frame_id_ = "map";
std::string map_name_;
// In order to do ROS2 stuff like creating a service we need a node:
rclcpp::Node::SharedPtr node_;
// A service to provide the occupancy grid map and the message with response:
rclcpp::Service<nav_msgs::srv::GetMap>::SharedPtr occ_service_;
nav_msgs::msg::OccupancyGrid map_msg_;
// Publish map periodically for the ROS1 via bridge:
rclcpp::TimerBase::SharedPtr timer_;
};
So what is the purpose of the MapFactory class?
To be more specific - what is the advantage of creating a class which holds a pointer of type Abstract class BaseMapServer which is a constructor (I believe) and this weird constructor creates a memory for the new object called OccGridServer and returns it? I got so confused by only writing this. I really want to become a better C++ coder and I am desperate to know the secret behind these code designs.
The MapFactory class is used to create the correct subclass instance of BaseMapServer based on the parameters passed to it.
In this particular case there is only one child class instance, but perhaps there are plans to add more. Then when more are added the factory method can look something like this:
BaseMapServer *CreateMap(
const std::string &map_type,
rclcpp::Node::SharedPtr node, const std::string &file_name)
{
if (map_type == "occupancy") return new OccGridServer(node, file_name);
// create Type2Server
else if (map_type == "type2") return new Type2Server(node, file_name);
// create Type3Server
else if (map_type == "type3") return new Type3Server(node, file_name);
else
{
RCLCPP_ERROR(node->get_logger(),
"map_factory.cpp 15: Cannot load map %s of type %s",
file_name.c_str(), map_type.c_str());
throw std::runtime_error("Map type not supported")
}
}
This has the advantage that the caller doesn't need to know the exact subclass being used, and in fact the underlying subclass could potentially change or even be replaced under the hood without the calling code needing to be modified. The factory method internalizes this logic for you.
Its a Factory pattern. See https://en.wikipedia.org/wiki/Factory_method_pattern. It looks like the current code only supports one implementation (OccGridServer), but more could be added at a future date. Conversely, if there's only ever likely to be one concrete implementation, then it's overdesign.
This is example of the factory design pattern. The use case is this: there are several types of very similar classes that will be used in code. In this case, OccGridServer is the only one actually shown, but a generic explanation might reference hypothetical Dog, Cat, Otter, etc. classes. Because of their similarity, some polymorphism is desired: if they all inherit from a base class Animal they can share virtual class methods like ::genus, ::species, etc., and the derived classes can be pointed to or referred to with base class pointers/references. In your case, OccGridServer inherits from BaseMapServer; presumably there are other derived classes as well, and pointers/references.
If you know which derived class is needed at compile time, you would normally just call its constructor. The point of the factory design pattern is to simplify selection of a derived class when the particular derived class is not known until runtime. Imagine that a user picks their favorite animal by selecting a button or typing in a name. This generally means that somewhere there's a big if/else block that maps from some type of I/O disambiguator (string, enum, etc.) to a particular derived class type, calling its constructor. It's useful to encapsulate this in a factory pattern, which can act like a named constructor that takes this disambiguator as a "constructor" parameter and finds the correct derived class to construct.
Typically, by the way, CreateMap would be a static method of BaseMapServer. I don't see why a separate class for the factory function is needed in this case.
Related
tl;dr
My goal is to conditionally provide implementations for abstract virtual methods in an intermediate workhorse template class (depending on template parameters), but to leave them abstract otherwise so that classes derived from the template are reminded by the compiler to implement them if necessary.
I am also grateful for pointers towards better solutions in general.
Long version
I am working on an extensible framework to perform "operations" on "data". One main goal is to allow XML configs to determine program flow, and allow users to extend both allowed data types and operations at a later date, without having to modify framework code.
If either one (operations or data types) is kept fixed architecturally, there are good patterns to deal with the problem. If allowed operations are known ahead of time, use abstract virtual functions in your data types (new data have to implement all required functionality to be usable). If data types are known ahead of time, use the Visitor pattern (where the operation has to define virtual calls for all data types).
Now if both are meant to be extensible, I could not find a well-established solution.
My solution is to declare them independently from one another and then register "operation X for data type Y" via an operation factory. That way, users can add new data types, or implement additional or alternative operations and they can be produced and configured using the same XML framework.
If you create a matrix of (all data types) x (all operations), you end up with a lot of classes. Hence, they should be as minimal as possible, and eliminate trivial boilerplate code as far as possible, and this is where I could use some inspiration and help.
There are many operations that will often be trivial, but might not be in specific cases, such as Clone() and some more (omitted here for "brevity"). My goal is to conditionally provide implementations for abstract virtual methods if appropriate, but to leave them abstract otherwise.
Some solutions I considered
As in example below: provide default implementation for trivial operations. Consequence: Nontrivial operations need to remember to override with their own methods. Can lead to run-time problems if some future developer forgets to do that.
Do NOT provide defaults. Consequence: Nontrivial functions need to be basically copy & pasted for every final derived class. Lots of useless copy&paste code.
Provide an additional template class derived from cOperation base class that implements the boilerplate functions and nothing else (template parameters similar to specific operation workhorse templates). Derived final classes inherit from their concrete operation base class and that template. Consequence: both concreteOperationBase and boilerplateTemplate need to inherit virtually from cOperation. Potentially some run-time overhead, from what I found on SO. Future developers need to let their operations inherit virtually from cOperation.
std::enable_if magic. Didn't get the combination of virtual functions and templates to work.
Here is a (fairly) minimal compilable example of the situation:
//Base class for all operations on all data types. Will be inherited from. A lot. Base class does not define any concrete operation interface, nor does it necessarily know any concrete data types it might be performed on.
class cOperation
{
public:
virtual ~cOperation() {}
virtual std::unique_ptr<cOperation> Clone() const = 0;
virtual bool Serialize() const = 0;
//... more virtual calls that can be either trivial or quite involved ...
protected:
cOperation(const std::string& strOperationID, const std::string& strOperatesOnType)
: m_strOperationID()
, m_strOperatesOnType(strOperatesOnType)
{
//empty
}
private:
std::string m_strOperationID;
std::string m_strOperatesOnType;
};
//Base class for all data types. Will be inherited from. A lot. Does not know any operations that might be performed on it.
struct cDataTypeBase
{
virtual ~cDataTypeBase() {}
};
Now, I'll define an example data type.
//Some concrete data type. Still does not know any operations that might be performed on it.
struct cDataTypeA : public cDataTypeBase
{
static const std::string& GetDataName()
{
static const std::string strMyName = "cDataTypeA";
return strMyName;
}
};
And here is an example operation. It defines a concrete operation interface, but does not know the data types it might be performed on.
//Some concrete operation. Does not know all data types it might be expected to work on.
class cConcreteOperationX : public cOperation
{
public:
virtual bool doSomeConcreteOperationX(const cDataTypeBase& dataBase) = 0;
protected:
cConcreteOperationX(const std::string& strOperatesOnType)
: cOperation("concreteOperationX", strOperatesOnType)
{
//empty
}
};
The following template is meant to be the boilerplate workhorse. It implements as much trivial and repetitive code as possible and is provided alongside the concrete operation base class - concrete data types are still unknown, but are meant to be provided as template parameters.
//ConcreteOperationTemplate: absorb as much common/trivial code as possible, so concrete derived classes can have minimal code for easy addition of more supported data types
template <typename ConcreteDataType, typename DerivedOperationType, bool bHasTrivialCloneAndSerialize = false>
class cConcreteOperationXTemplate : public cConcreteOperationX
{
public:
//Can perform datatype cast here:
virtual bool doSomeConcreteOperationX(const cDataTypeBase& dataBase) override
{
const ConcreteDataType* pCastData = dynamic_cast<const ConcreteDataType*>(&dataBase);
if (pCastData == nullptr)
{
return false;
}
return doSomeConcreteOperationXOnCastData(*pCastData);
}
protected:
cConcreteOperationXTemplate()
: cConcreteOperationX(ConcreteDataType::GetDataName()) //requires ConcreteDataType to have a static method returning something appropriate
{
//empty
}
private:
//Clone can be implemented here via CRTP
virtual std::unique_ptr<cOperation> Clone() const override
{
return std::unique_ptr<cOperation>(new DerivedOperationType(*static_cast<const DerivedOperationType*>(this)));
}
//TODO: Some Magic here to enable trivial serializations, but leave non-trivials abstract
//Problem with current code is that virtual bool Serialize() override will also be overwritten for bHasTrivialCloneAndSerialize == false
virtual bool Serialize() const override
{
return true;
}
virtual bool doSomeConcreteOperationXOnCastData(const ConcreteDataType& castData) = 0;
};
Here are two implementations of the example operation on the example data type. One of them will be registered as the default operation, to be used if the user does not declare anything else in the config, and the other is a potentially much more involved non-default operation that might take many additional parameters into account (these would then have to be serialized in order to be correctly re-instantiated on the next program run). These operations need to know both the operation and the data type they relate to, but could potentially be implemented at a much later time, or in a different software component where the specific combination of operation and data type are required.
//Implementation of operation X on type A. Needs to know both of these, but can be implemented if and when required.
class cConcreteOperationXOnTypeADefault : public cConcreteOperationXTemplate<cDataTypeA, cConcreteOperationXOnTypeADefault, true>
{
virtual bool doSomeConcreteOperationXOnCastData(const cDataTypeA& castData) override
{
//...do stuff...
return true;
}
};
//Different implementation of operation X on type A.
class cConcreteOperationXOnTypeASpecialSauce : public cConcreteOperationXTemplate<cDataTypeA, cConcreteOperationXOnTypeASpecialSauce/*, false*/>
{
virtual bool doSomeConcreteOperationXOnCastData(const cDataTypeA& castData) override
{
//...do stuff...
return true;
}
//Problem: Compiler does not remind me that cConcreteOperationXOnTypeASpecialSauce might need to implement this method
//virtual bool Serialize() override {}
};
int main(int argc, char* argv[])
{
std::map<std::string, std::map<std::string, std::unique_ptr<cOperation>>> mapOpIDAndDataTypeToOperation;
//...fill map, e.g. via XML config / factory method...
const cOperation& requestedOperation = *mapOpIDAndDataTypeToOperation.at("concreteOperationX").at("cDataTypeA");
//...do stuff...
return 0;
}
if you data types are not virtual (for each operation call you know both operation type and data type at compile time) you may consider following approach:
#include<iostream>
#include<string>
template<class T>
void empty(T t){
std::cout<<"warning about missing implementation"<<std::endl;
}
template<class T>
void simple_plus(T){
std::cout<<"simple plus"<<std::endl;
}
void plus_string(std::string){
std::cout<<"plus string"<<std::endl;
}
template<class Data, void Implementation(Data)>
class Operation{
public:
static void exec(Data d){
Implementation(d);
}
};
#define macro_def(OperationName) template<class T> class OperationName : public Operation<T, empty<T>>{};
#define macro_template_inst( TypeName, OperationName, ImplementationName ) template<> class OperationName<TypeName> : public Operation<TypeName, ImplementationName<TypeName>>{};
#define macro_inst( TypeName, OperationName, ImplementationName ) template<> class OperationName<TypeName> : public Operation<TypeName, ImplementationName>{};
// this part may be generated on base of .xml file and put into .h file, and then just #include generated.h
macro_def(Plus)
macro_template_inst(int, Plus, simple_plus)
macro_template_inst(double, Plus, simple_plus)
macro_inst(std::string, Plus, plus_string)
int main() {
Plus<int>::exec(2);
Plus<double>::exec(2.5);
Plus<float>::exec(2.5);
Plus<std::string>::exec("abc");
return 0;
}
Minus of this approach is that you'd have to compile project in 2 steps: 1) transform .xml to .h 2) compile project using generated .h file. On plus side compiler/ide (I use qtcreator with mingw) gives warning about unused parameter t in function
void empty(T t)
and stack trace where from it was called.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Ok so I have tried implementing simple mono alphabetic substitution ciphers like Caesars , digraph like playfair , polyalphabetic ones like autokey, vigenre and a few others in c++ {without using classes}. Now i would like to bring together all these ciphers and a few others and package it into a single project.I have started coding a few lines, but i'm not sure how i must design it. Here's how my classes look.
my front end
//main.cpp contains few switch cases to chose the right cipher for encryption.
//cipher.cpp implements class cipher.In a crude format the class looks like
class cipher
{
protected:
string plaintxt,ciphertxt;
public:
virtual bool encrypt()=0;
virtual bool decrypt()=0;
virtual bool tabulate()=0;
}
this class is interfaced by cipher.h
//mono_alphabetic.cpp implemants the class mono_alpha
class mono_alpha : public cipher
{
protected:
map<string,string> Etable,Dtable;
public:
bool Encrypt();
bool Decrypt();
}
Now i'm using a simple example of atbash cipher here.For those of you who don't know what an atbash cipher is, it is a mode of encryption in which each character in a given string is encrypted with its equivalent character as per position in the reverse alphabetic order. For eg. A ->Z Z->A B->Y M->N so on.
class atbash : public mono_alpha
{
public:
bool tabulate(); // makes a hash map were A is mapped to Z M to N e.t.c
atbash(string&); // accepts a string and copies it to string plaintxt.
}
This is a very crude example. Only the class design is presented here.Here are a few doubts of mine.
implemantation : I would accepts a string from the user and then pass it to the constructor of class atbash, where it is copied to the string data member plaintxt inherited from the base class cipher. Then i would invoke the function tabulate from the constructor.Now i have two choices either tabulate() generates a hash map of encryption table and store it in Etable, or it could also generate the decryption table.In the case of an atbash cipher these are one but the same. But what about the case of a general mono alphabetic substitution cipher ? how would i force the tabulate functio to create either one.
my idea was to pass a character argument to the constructor to the constructor which describes if the given string is to be encrypted or decrypted and accordingly saves it in either one among plaintxt or ciphertxt.Further the constructor passes this character argument to tabulate function which tabulates the encryption or decryption table accordingly.Is this good ?
any suggestion on how to improve this ?
interface : my way of implementing an interface to all these ciphers from main.cpp was to use swith case like.
switch(chosen_value)
{
case 1: cout<<"atbash encryption";
cipher*ptr = new atbash ("a string");
// ptr->tabulate(); if it isn't being called directly from the constructor.(here it is)
case 2:
cout<< "caeser's cipher";
.....................
.
.....
}
Are there any better ways to implement this without using switch case.
also as you can see i have used a base class pointer to an object of the derived class for doing this.I know it isn't necessary here and the I can simply proceed by declaring an object. Is there any real importance to referencing objects through a base class pointer ?
I have heard that these base class pointers can be a real life savior sometimes ! If so please direct me on scenarios where this simplifies coding . Is declaring pure virtual functions in the base class not of any use in this particular case.Is it just bloating the code here ?
should i go on with separating the class implementations into separate files like i have done here or should i just cramp up all these code in a single main.cpp which would make inheritance a lot easier as you don't have to use header files.
Please guide me on this.I have zero professional experience in coding and would love to here your opinions.
Some ideas, in no particular order
Have different classes for encryption and for decryption. That will solve your doubt on what to use. So the cipher base class becomes a base class for a transformation of a string into other. (not an expert on patterns, but I believe this is the Command Pattern)
The nice thing about having an object to represent the algorithm is that it can have state. You might want to add a reset() method to be able to reuse the object on a new execution if the creation of the object is expensive.
You can make the base class a function object with an abstract operator(). This operator() gets implemented in each specific encryption and descryption classes. Using this allows you to handle this classes as functions (the downside is that it is perhaps less clear what you're doing).
It is correct to handle everything through pointers to the base class (or references or smart pointers)
In order to create the right type of operation, have a Factory class (this is again a pattern). This can be a class with a creator method where you indicate the algorithm and the encryption/decryption direction. The Factory returns a pointer to the base class, pointing the appropriate implementation.
The implementation can be a vector or a map or an array of some specific factory objects (whose job is to instantiate an algorithm object of the different types)... Alternative you can have a static method on each derived class and store a pointer to method in the structure.
The structure (vector/map/array/whatever) is used for fast selection of the right algorithm. If the number of algorighms is small, the use of a switch statement is probably fine. The structure is contained in the Factory class and initialize on its constructor.
You must mind the lifecycle of the objects created. Objects are created by the Factory, but who should destroy them?
Consider what you're going to use to represent the encrypted/decrypted messages and wether they become non-printable or they can become too large.
There are many design decisions here, many trade offs that depends on different things.
Hope the above lines give you some ideas to start.
Edit: adding a more concrete example
We will start with an Operation class. This assumes that we can have both encrypters and decrypters can be called with the same API
class Operation {
public:
virtual ~Operation() { }
virtual std::string operator()(const std::string &input)=0;
virtual void reset() { }
};
Notes on this:
Assumes that the API is string input gives a string output. This is the operator() pure virtual method.
Added a virtual destructor. We're going to be dealing mostly with references to Operation. However implementations of the algorithm my need to destroy their own things, so the destructor must be virtual so that when deleting an Operation pointer it will also invoke the destructor of the derived class.
Added a reset() method. This has a default implementation that does nothing. Potentially derived classes might store state, this method is intended to return the operation to its initial step so that you don't have to scrap it and create another.
Now some of the derived classes:
class MyEncoder: public Operation {
public:
static Operation *create() {
return new MyEncoder();
}
std::string operator()(const std::string &input) {
// Do things.
return std::string();
}
};
class MyDecoder: public Operation { ... };
class OtherEncoder: public Operation { ... };
class OtherDecoder: public Operation { ... };
I'm only showing in full MyEncoder We see a static method create that we will talk about later.
The implementation of the algorithm happens on the implementation of the operator()
You could:
Keep state in attributes of MyEncoder
Initialize stuff on constructor
... and perhaps destroy things in a destructor.
Potentially include an implementation of the reset() method to reuse the object in another invocation.
Now for the Factory:
class OperationFactory {
public:
enum OperationDirection {
OD_DECODER=0,
OD_ENCODER
};
enum OperationType {
OT_MY=0,
OT_OTHER
};
....
};
Just declared the class and a couple of enumerations to help us distinguish between encoders and decoders and the two algorithm I'm going to use.
We need some place to store things, so the Factory class ends with:
class OperationFactory {
public:
...
private:
typedef Operation *(*Creator)();
typedef std::map<OperationType,Creator> OperationMap;
OperationMap mEncoders;
OperationMap mDecoders;
};
Here:
The first typedef gives a name to a function pointer. This is a function that takes no arguments and returns a pointer to an Operation. A static method is the same as a function (at least regarding function pointers)... so this typedef allows us to give a name to the mysterious create() static method we had above.
The second typedef is just shortcut for the lengthy std::map definition. This is a map from OperatonType to Creator function.
We define two of those maps, one for Encoder, one for Decoders. You could devise a different way.
With that we can provide some methods for the user to obtain what it wants:
class OperationFactory {
public:
...
Operation *getOperation(OperationDirection _direction,OperationType _type) const {
switch(_direction) {
case OD_DECODER:
return getDecoder(_type);
case OD_ENCODER:
return getEncoder(_type);
default:
// Or perhaps throw an exception
return 0;
}
}
Operation *getEncoder(OperationType _type) const {
OperationMap::const_iterator it=mEncoders.find(_type);
if(it!=mEncoders.end()) {
Creator creator=it->second;
return (*creator)();
} else {
// Or perhaps throw an exception
return 0;
}
}
Operation *getDecoder(OperationType _type) const {
.... // similar but over the mDecoders
}
....
};
So, we look up the OperationType in the map and get a pointer to a function (Creator) type, we can call this function (*creator)() to obtain the instance of the Operation that we requested.
Some words on (*creator)():
creator is of type Creator... so it is a pointer to a function.
(*creator) is the function (the same as if p is an int *, *p is of type int)...
(*creator)() is the invocation of a function.
To complete this we need to really have something in the map... so we add that on the constructor:
class OperationFactory {
public:
....
OperationFactory() {
mEncoders[OT_MY]=&MyEncoder::create;
mEncoders[OT_MY]=&MyDecoder::create;
mEncoders[OT_OTHER]=&OtherEncoder::create;
mEncoders[OT_OTHER]=&OtherDecoder::create;
}
....
};
We insert for each algorithm the pointer to their create static methods.
Finally how do we use it?
int main(int argc,char **argv) {
OperationFactory f;
Operation *o=f.getOperation(OperationFactory::OD_DECODER,OperationFactory::OT_MY);
std::string toTransform="Hello world";
std::string transformed=(*o)(toTransform);
delete o; // don't forget to delete it.
}
Here we have an instance of the OperationFactory f from where we can request the creation of our desired operation with the getOperation() methods.
The object that we got can be used to execute the algorithm. Note that (*o)(toTransform) is formaly similar to our invocation of creator above, but there are differences:
o is a pointer to an object of type Operation (actually is really a pointer MyEncoder)
(*o) is an object of typeOperation(well, really of typeMyEnconder`)
(*o)(toTransform) is the invocation of the operator() method of the MyEncoder type.
We could have used this technique on the Creator: using an object-function instead of a pointer to function... but it would have been more code.
Note that the factory allocates memory... and this memory must be disposed when no longer needed. Ways of not doing this are to use unique_ptr or shared_ptr...
Note that getOperation() could return a null pointer when it cannot find the algorithm requested... so the calling code should check for that possibility.
Alternatively the implementation of getOperation() could have chosen to throw an exception when the algorithm is not found... again the calling code should then have had a try/catch.
Now, how to add a new algorithm:
Derive and implement your encoder and decoder classes from Operation
Expand the enum OperationType
register the creators in the OperationFactory constructor.
... use it.
Let's say I have a string with my class name inside:
mystring = "CDialogChild";
I would like to create an object of the type name written in my string.
I'd like to do something like:
CDialogParent dlg = CDialogParent(mystring);
My new dlg is now created as type CDialogChild and I can use its methods:
dlg.mycdialogchild_method();
There is nothing in C++ that will provide you such feature (called Reflection).
However, if your classes are of finite number, you can do some kind of mapping with some factories :
class IClassFactory // base interface
{ public:
virtual ~IClassFactory(){}
}
template< class T >
class ClassFactory {
/* whatever necessary here*/
public:
T* create();
};
class ClassManager
{
public:
void add( std::string name, IClassFactory* factory ) { m_map[name] = factory; }
ACommonBaseClass* create( std::string class_name ) { return m_map[class_name]->create(); } // this line isn't correct but you get the idea
private:
std::map< std::string, IClassFactory* > m_map;
};
Or something similar (this is written quickly).
Alternatively you could work with a scripting language that would allow Reflection, but that would add a whole layer to your application. Scripting languages that might be of interest for embedding with c++ : ChaiScript, Falcon, Lua, Python, AngelScript, MonkeyScript, Io, ...
In general this is not possible within the language, since C++ is not a reflective language and it is statically typed.
As an alternative, you could consider hardcoding a collection of related, polymorphic types with a common base class into the program and writing a factory method that creates the desired concrete instance based on a runtime parameter.
There is a design pattern for completing your objective: Factory.
You will need to compare the class name and return the object:
class MyObject;
MyObject * creator(const std::string& object_name)
{
return (object_name == "MyObject") ? new MyObject : 0;
}
The Factory pattern differs a little in that it uses pointers to a base class and returns instances of child classes.
I'm wondering, is it possible in C++ to use a text value read in from a file to create an object of a class of that name eg.
contents of file: "MyClass"
code: read file
code: instantiate "MyClass" object.
I'd like to avoid a whole series of hardcoded if/then/elses if possible.
Sorry I'm not sure how to describe this problem in more technical terms!
As long as you don't mind some restrictions, this is fairly easy to do. The easiest way to do the job restricts you to classes that descend from one common base class. In this case, you can do something like this:
// warning: I've done this before, but none of this code is tested. The idea
// of the code works, but this probably has at least a few typos and such.
struct functor_base {
virtual bool operator()() = 0;
};
You'll then obviously need some concrete classes derived from that base:
struct eval_x : functor_base {
virtual bool operator()() { std::cout << "eval_x"; }
};
struct eval_y : functor_base {
virtual bool operator()() { std::cout << "eval_y"; }
};
Then we need some way to create an object of each type:
functor_base *create_eval_x() { return new eval_x; }
functor_base *create_eval_y() { return new eval_y; }
Finally, we need a map from the names to the factory functions:
// the second template parameter is:
// pointer to function returning `functor_base *` and taking no parameters.
std::map<std::string, functor_base *(*)()> name_mapper;
name_mapper["eval_x"] = create_eval_x;
name_mapper["eval_y"] = create_eval_y;
That (finally!) gives us enough so we can map from a name to a function object:
char *name = "eval_x";
// the map holds pointers to functions, so we need to invoke what it returns
// to get a pointer to a functor:
functor_base *b = name_mapper.find(name)();
// now we can execute the functor:
(*b)();
// since the object was created dynamically, we need to delete it when we're done:
delete b;
There are, of course, many variations on the general theme. For example, instead of factory functions that create objects dynamically, you can create an instance of each object statically, and just put the address of the static object in the map.
you can use abstract factory to do this. Limitations are the classes need to implement a base class and you need to register the factory class with the abstract factory.
class AbstractFactory;
class Factory;
class Object;
// base marker class for all the classes that need this instantiation strategy
class Object{}
class Factory
{
public:
//override this in concrete factories
virtual Object* create() = 0;
};
// helper macro to declare an inner class that's a default factory
#define DECL_DEFAULT_FACTORY(ObjectClass) class Factory : public Factory \
{\
public:\
Factory(){}\
virtual ~Factory(){}\
Object* create(){ return new ObjectClass(); } \
}
// this can be made a singleton
class AbstractFactory
{
public:
void registerClass(const String& clsName, Factory* factory){ //put this in a std::map }
void deregisterClass(const String& className){ //remove factory from map and delete the ptr }
Object* create(const String& className)
{
Factory* factory = factories[className];
if(factory){ return factory->create(); }
else{ return 0; }
}
};
so it shall be used like:
class SampleClass : public Object
{
//impl goes here
DECL_DEFAULT_FACTORY(SampleClass);
}
and somewhere else you need to do
abstractFactoryInstance->registerClass("SampleClass",new SampleClass::Factory());
then your AbstractFactory is ready to do
SampleClass* obj = (SampleClass*)(abstractFactoryInstance->create("SampleClass"));
you can further refine the scheme by declaring template classes for Factory
Most C++ frameworks (e.g., MFC, Boost) support some kind of object serialization, though it doesn't generally take the form of a human-readable text file. In C++, all of the class types whose instances are recorded in a file have to be present (i.e., compiled and linked into) a program in order for that program to either write or read (and create objects from) such a file.
With Compiled languages like C++, you won't be able to do so.
Such things are only possible with interpreted languages, such as PHP.
What you need is boost serializarion. This will allow you to persist your objects in a file. You can find an example here.
I have an interesting problem. Consider this class hierachy:
class Base
{
public:
virtual float GetMember( void ) const =0;
virtual void SetMember( float p ) =0;
};
class ConcreteFoo : public Base
{
public:
ConcreteFoo( "foo specific stuff here" );
virtual float GetMember( void ) const;
virtual void SetMember( float p );
// the problem
void foo_specific_method( "arbitrary parameters" );
};
Base* DynamicFactory::NewBase( std::string drawable_name );
// it would be used like this
Base* foo = dynamic_factory.NewBase("foo");
I've left out the DynamicFactory definition and how Builders are
registered with it. The Builder objects are associated with a name
and will allocate a concrete implementation of Base. The actual
implementation is a bit more complex with shared_ptr to handle memory
reclaimation, but they are not important to my problem.
ConcreteFoo has class specific method. But since the concrete instances
are create in the dynamic factory the concrete classes are not known or
accessible, they may only be declared in a source file. How can I
expose foo_specific_method to users of Base*?
I'm adding the solutions I've come up with as answers. I've named
them so you can easily reference them in your answers.
I'm not just looking for opinions on my original solutions, new ones
would be appreciated.
The cast would be faster than most other solutions, however:
in Base Class add:
void passthru( const string &concreteClassName, const string &functionname, vector<string*> args )
{
if( concreteClassName == className )
runPassThru( functionname, args );
}
private:
string className;
map<string, int> funcmap;
virtual void runPassThru( const string &functionname, vector<string*> args ) {}
in each derived class:
void runPassThru( const string &functionname, vector<string*> args )
{
switch( funcmap.get( functionname ))
{
case 1:
//verify args
// call function
break;
// etc..
}
}
// call in constructor
void registerFunctions()
{
funcmap.put( "functionName", id );
//etc.
}
The CrazyMetaType solution.
This solution is not well thought out. I was hoping someone might
have had experience with something similar. I saw this applied to the
problem of an unknown number of a known type. It was pretty slick. I
was thinking to apply it to an unkown number of unknown type***S***
The basic idea is the CrazyMetaType collects the parameters is type
safe way, then executing the concrete specific method.
class Base
{
...
virtual CrazyMetaType concrete_specific( int kind ) =0;
};
// used like this
foo->concrete_specific(foo_method_id) << "foo specific" << foo_specific;
My one worry with this solution is that CrazyMetaType is going to be
insanely complex to get this to work. I'm up to the task, but I
cannot count on future users to be up to be c++ experts just to add
one concrete specific method.
Add special functions to Base.
The simplest and most unacceptable solution is to add
foo_specific_method to Base. Then classes that don't
use it can just define it to be empty. This doesn't work because
users are allowed to registers their own Builders with the
dynamic_factory. The new classes may also have concrete class
specific methods.
In the spirit of this solution, is one slightly better. Add generic
functions to Base.
class Base
{
...
/// \return true if 'kind' supported
virtual bool concrete_specific( int kind, "foo specific parameters" );
};
The problem here is there maybe quite a few overloads of
concrete_specific for different parameter sets.
Just cast it.
When a foo specific method is needed, generally you know that the
Base* is actually a ConcreteFoo. So just ensure the definition of class
ConcreteFoo is accessible and:
ConcreteFoo* foo2 = dynamic_cast<ConcreteFoo*>(foo);
One of the reasons I don't like this solution is dynamic_casts are slow and
require RTTI.
The next step from this is to avoid dynamic_cast.
ConcreteFoo* foo_cast( Base* d )
{
if( d->id() == the_foo_id )
{
return static_cast<ConcreteFoo*>(d);
}
throw std::runtime_error("you're screwed");
}
This requires one more method in the Base class which is completely
acceptable, but it requires the id's be managed. That gets difficult
when users can register their own Builders with the dynamic factory.
I'm not too fond of any of the casting solutions as it requires the
user classes to be defined where the specialized methods are used.
But maybe I'm just being a scope nazi.
The cstdarg solution.
Bjarn Stroustrup said:
A well defined program needs at most few functions for which the
argument types are not completely specified. Overloaded functions and
functions using default arguments can be used to take care of type
checking in most cases when one would otherwise consider leaving
argument types unspecified. Only when both the number of arguments and
the type of arguments vary is the ellipsis necessary
class Base
{
...
/// \return true if 'kind' supported
virtual bool concrete_specific( int kind, ... ) =0;
};
The disadvantages here are:
almost no one knows how to use cstdarg correctly
it doesn't feel very c++-y
it's not typesafe.
Could you create other non-concrete subclasses of Base and then use multiple factory methods in DynamicFactory?
Your goal seems to be to subvert the point of subclassing. I'm really curious to know what you're doing that requires this approach.
If the concrete object has a class-specific method then it implies that you'd only be calling that method specifically when you're dealing with an instance of that class and not when you're dealing with the generic base class. Is this coming about b/c you're running a switch statement which is checking for object type?
I'd approach this from a different angle, using the "unacceptable" first solution but with no parameters, with the concrete objects having member variables that would store its state. Though i guess this would force you have a member associative array as part of the base class to avoid casting to set the state in the first place.
You might also want to try out the Decorator pattern.
You could do something akin to the CrazyMetaType or the cstdarg argument but simple and C++-ish. (Maybe this could be SaneMetaType.) Just define a base class for arguments to concrete_specific, and make people derive specific argument types from that. Something like
class ConcreteSpecificArgumentBase;
class Base
{
...
virtual void concrete_specific( ConcreteSpecificArgumentBase &argument ) =0;
};
Of course, you're going to need RTTI to sort things out inside each version of concrete_specific. But if ConcreteSpecificArgumentBase is well-designed, at least it will make calling concrete_specific fairly straightforward.
The weird part is that the users of your DynamicFactory receive a Base type, but needs to do specific stuff when it is a ConcreteFoo.
Maybe a factory should not be used.
Try to look at other dependency injection mechanisms like creating the ConcreteFoo yourself, pass a ConcreteFoo type pointer to those who need it, and a Base type pointer to the others.
The context seems to assume that the user will be working with your ConcreteType and know it is doing so.
In that case, it seems that you could have another method in your factory that returns ConcreteType*, if clients know they're dealing with concrete type and need to work at that level of abstraction.