c++ pass different classes into function - c++

I may be going about this completely the wrong way but it's getting late and its driving me a little mad.
I have a function in my base class that should allow me to create new objects of derived classes. However where it's says Melee I want it to be interchangeable with my other derived classes. I can't figure how to pass in a different class so it will create an object of that class.
void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector)
{
UnitID++;
MyVector.push_back(new Melee());
}

You can use the "prototype design pattern".
Put in a std::map the associations key -> prototype and you're done: when the user press a key, you lookup into the map, extract the prototype and pass it to CreateNewUnit. Inside CreateNewUnit, you will use Prototype::Clone()
class BaseUnit
{
public:
... // your methods
virtual BaseUnit* Clone() = 0;
};
class Melee : public BaseUnit
{
public:
BaseUnit* Clone() { return new Melee(); }
};
void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, BaseUnit* prototype )
{
UnitID++;
MyVector.push_back( prototype -> Clone() );
}
int main()
{
std::map< char, BaseUnit* > prototypes;
prototypes[ 'a' ] = new Melee();
prototypes[ 'b' ] = new OtherUnit();
...
std::vector< BaseUnit* > units;
char selection = GetKeyPressed();
BaseUnit::CreateNewUnit( units, prototypes[ selection ] );
}
The benefit of this solution is that when you need to add a new Unit type in your application you must just add a new entry in the prototypes map.
And by the way: with a little effort you can even avoid that, if the classes derived from BaseUnit register themselves in a global "prototypes" map.

I would either pass a pointer to the unit:
void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, BaseUnit* unit)
{
UnitID++;
MyVector.push_back(unit);
}
or pass the key pressed or some other id:
void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, char type)
{
switch(type) //this would be the key pressed
{
case '1':
MyVector.push_back(new Melee());
UnitID++;
break;//this would be continued for all possible units
}
}

If you want it to be determined at compile time you can either use templates methods or create a method for each class. This is called factory method, it's a design pattern. Note that the 'hint' to the factory here is the template parameter.
If you want it to be determined at runtime, you need to identify the relevant class in some way, a 'hint' for the factory. Most of the times this would be an enum or a string. Again, read about the factory design pattern and factory method.

This probably gives you an idea of how to do it:
1. change the function signature to include the derived object pointer as a second parameter,
void BaseUnit::CreateNewUnit(vector<BaseUnit*>& MyVector, const BaseUnit* derived) {
UnitID++;
MyVector.push_back(derived);
}
Call the above function when you press a key, if a key is pressed, create a new derived object and call ur function:
Melee *m = new Melee();
unit.CreateNewUnit(myVector,m);

If you need that at compile time, you can simply go for a factory template. If different classes have different parameters that need to be passed, you should use a variadic template:
#include <utility>
class Factory {
...
template <typename Derived, typename ...Args>
BaseUnit * create(Args &&... args)
{
return new Derived(std::forward<Args>(args)...);
}
};
Then you can simply create a factory and call factory.create(Melee, optional_arguments) or whatever type, and a template specialization will be created to instantiate that particular type with the supplied arguments.
If you need a "runtime dispatch" creation function, you could have something like an enum Type in BaseUnit or some other id that lists every possible derived type, and have a create(BaseUnit::Type type) method with a switch (type) statement and a case for every object type that creates the appropriate type. Naturally, this can be used in conjunction with the template factory listed above.
A switch statement will be considerably more efficient than a map, and besides, a map will not really make sense if it is populated "statically". A map only makes sense when you want to register a new object creation method during the runtime. In that case you can have a map of a type id and a pointer to a function, returning a new BaseType derived. This way you can for example register extra functions, loaded from a dynamic library, look up the function pointer based on the type id and use it to instantiate object of that type. But that's only in case you need runtime registration. Otherwise stick to the previous solutions. This will only be justified if you have something like "plugins" - dynamic libraries which provide a set of extra types and the appropriate functions to create them, so you can load a library, register its components in the map, and be able to instantiate types which are not present during the implementation of the "core logic" of the game. Naturally, this all has to rely on polymorphic interfaces that the core logic is design around in order to work, otherwise it won't. Also, for this solution you might want the id to be a string rather than an integer, since it offers more flexibility and does not have the restrictions of a switch statement.
Also note I did not include your vector<BaseUnit*>& MyVector - IMO that shouldn't be an argument you pass, but a local member to your game class which you can access directly without needing to have it as an argument.
Also, follow proper coding conventions, do not use member methods or objects or parameters with upper case. It is just confusing.

Related

How to create user defined type template class object C++

I am struggling with allowing user to select data type template will be created as.
Since template type must be defined on compile, I must specify data type template will use eg(string,int, so on), but that means I cannot change it latter on, from lets say string to int even if my template supports it, because template class object was declared as string.
My class declaration below:
template <class T>
class MyHashTable
{
public:
string deleted="deleted";
unsigned short tableSize;
// array of vectors, hash table container
vector<T>* myTable;
vector<T>* deletionTable;
MyHashTable(unsigned short tableSize) : myTable(new vector<T>[tableSize]), deletionTable(new vector<T>[tableSize])
{
this->tableSize=tableSize;
}
object declaration outside class
MyHashTable <string>* myChainedTable=NULL ;
string tableType;
object initialization
if (myChainedTable)
{
delete myChainedTable;
myChainedTable=NULL;
}
getType();
if (!myChainedTable)
{
if (tableType=="string")
myChainedTable= new MyHashTable<string>(length);
if (tableType=="char")
MyHashTable<char> myChainedTable(length); // no difference with or without using new keyword
if (tableType=="double")
MyHashTable<double> myChainedTable(length);
if (tableType=="float")
MyHashTable<float> myChainedTable(length);
if (tableType=="int")
MyHashTable<int> myChainedTable(length);
cout<<tableType<<" table of size "<< length<<" created"<<endl;
I attempted passing class object to functions instead of having it as global variable, but couldnt get it work either.
What I really need is single template object that can have: int,string,char,double,float types, I have 3 functions that need to have access to template class object, and having 5 different objects and 200 lines of if statements for each situation sounds like worst possible solution.
I been stuck on this for a while and just cant figure out how to do it and any help will be appreciated.
void getType()
{
cout<<"Enter table type, types available: int, char, float, double, string.\n";
tableType=getInput();
while((tableType != "int")&&(tableType !="float")&&(tableType !="double")&&(tableType!="char")&&(tableType !="string"))
{
cout<<"Invalid type, please try again "<<endl;;
tableType=getInput();
}
}
Your question is at the boarder between templates and variants.
The template is compile time. So you have to choose at compile time the type you want for your object. Your conditional approach can't work (see comments to question).
On the other side, you seem to need a dynamic choice of type at runtime.
If you want to go on on template way: (edit based on comments)
You'd need to have all the templates inherit from a single polymorphic base class (one common interface with virtual functions). Example:
class MyHashBase // common base class for all templates
{
public:
virtual void addElement(void *ptrelem) = 0; // adding an element must be implemented by template. With void* since future template type unknown from base class
virtual void displayAll() = 0;
};
The templates would need then implement the virtual functions:
template <class T>
class MyHashTable : public MyHashBase
{
public:
unsigned short tableSize;
vector<T>* myTable; // I leave it as it is, but you could implement these as vector<T> instead of vector<T>*
vector<T>* deletionTable;
MyHashTable(unsigned short tableSize) : myTable(new vector<T>[tableSize]), deletionTable(new vector<T>[tableSize]), tableSize(tableSize)
{ }
void addElement(void* ptrelem)
{ myTable->push_back(*reinterpret_cast<T*>(ptrelem)); } // reinterpret the void* of the common interface as a T*
void displayAll()
{ copy(myTable->begin(), myTable->end(), ostream_iterator<T>(cout, "\n")); }
};
You could then have your myChainedTable be a pointer to the common base type, and intialise this pointer in the way you did with the string case (i.e. using new).
MyHashBase *myChainedTable = nullptr;
//...
if (tableType == "string")
myChainedTable = new MyHashTable<string>(length);
else if (tableType == "double")
myChainedTable = new MyHashTable<double>(length);
//...
You could then use the common API, for example if tableType is "double":
double d1 = 3.1415, d2 = 1.4142;
myChainedTable->addElement(&d1); // ATTENTION: you must ensure to provide pointer to the correct data type
myChainedTable->addElement(&d2);
myChainedTable->displayAll();
You'll certainly have a coupe of if required in the calling code, but you could reduce them to minimum by carefully designing the base class (for example, you could add a virtual clone function, to duplicate the data without need to know the type by the caller).
However, using a single signature for the common functions of the base class is cumbersome. To make the virtualisation possible you need to pass parameters through void* pointer which is not so nice and rather error prone.
Alternate way with variants
You could also use boost variants which are meant for managing objects with dynamic definition of types.
In this case you would not need template for your own data structure. You would create a MyHashTable with elements of type boost::variant< int, std::string, ... >.
You could then access to the right value of the object if you know its type (as in your myChainedTable) by using: boost::get<int> (element) (or boost::get<string>(), ...).
If you don't know the type on an element you could use the concept of "visitor" to chose automatically the appropriate function to exectue depending on the type.
Edit: alternate way with unions:
If you're not allowed to use variants another alternative could be use a union. I don't know the topic of you rassignment, but you have the choice whether you use a union to define the elements (like the variants, without templates) or to use a template type as you did, but define myChainedTable to be a union of pointers to the different template instantiations. But yes, it requires a lot of ifs...
Templates are resolved at compile time. Your container type is resolved at runtime. Templates are clearly not the solution here. The first thing that comes to my mind is a combination of boost::any and std::vector instead.

Dynamic mapping of enum value (int) to type

It appeared that this problem is quite common in our job.
We we are sending an int or enum value through the network, then we receive it we would like to create/call a particular object/function.
The most simply solution would be to use the switch statement, like below:
switch (value) {
case FANCY_TYPE_VALUE: return new FancyType();
}
It works fine, but we would have plenty of these switch blocks, and when we create new value and type, we would need to change all of them. It does seem right.
Other possibility would be to use the templates. But we cannot, since the value of enum is defined in runtime.
Is there any right design pattern for that, or any right approach?
It seems like a very general and common problem in every day coding...
Try a map:
struct Base { };
struct Der1 : Base { static Base * create() { return new Der1; } };
struct Der2 : Base { static Base * create() { return new Der2; } };
struct Der3 : Base { static Base * create() { return new Der3; } };
std::map<int, Base * (*)()> creators;
creators[12] = &Der1::create;
creators[29] = &Der2::create;
creators[85] = &Der3::create;
Base * p = creators[get_id_from_network()]();
(This is of course really crude; at the very least you'd have error checking, and a per-class self-registration scheme so you can't forget to register a class.)
You can actually do this with some template trickery:
#include <map>
template <typename Enum, typename Base>
class EnumFactory {
public:
static Base* create(Enum e) {
typename std::map<Enum,EnumFactory<Enum,Base>*>::const_iterator const it = lookup().find(e);
if (it == lookup().end())
return 0;
return it->second->create();
}
protected:
static std::map<Enum,EnumFactory<Enum,Base>*>& lookup() {
static std::map<Enum,EnumFactory<Enum,Base>*> l;
return l;
}
private:
virtual Base* create() = 0;
};
template <typename Enum, typename Base, typename Der>
class EnumFactoryImpl : public EnumFactory<Enum,Base> {
public:
EnumFactoryImpl(Enum key)
: position(this->lookup().insert(std::make_pair<Enum,EnumFactory<Enum,Base>*>(key,this)).first) {
}
~EnumFactoryImpl() {
this->lookup().erase(position);
}
private:
virtual Base* create() {
return new Der();
}
typename std::map<Enum,EnumFactory<Enum,Base>*>::iterator position;
};
This allows you to create a new derived object from a given enum, by saying
// will create a new `FancyType` object if `value` evaluates to `FANCY_TYPE_VALUE` at runtime
EnumFactory<MyEnum,MyBase>::create(value)
However, you have to have some EnumFactoryImpl objects, which could be static in some function or namespace.
namespace {
EnumFactoryImpl<MyEnum,MyBase,Derived1> const fi1(ENUM_VALUE_1);
EnumFactoryImpl<MyEnum,MyBase,Derived2> const fi2(ENUM_VALUE_2);
EnumFactoryImpl<MyEnum,MyBase,Derived3> const fi3(ENUM_VALUE_3);
EnumFactoryImpl<MyEnum,MyBase,FancyType> const fi1(FANCY_TYPE_VALUE); // your example
}
These lines are the single point where your source code maps enum values to derived types. So you have everything at the same location, and no redundancy (this eliminates the problem of forgetting to change it in some places, when adding new derived types).
One option is to maintain a dictionary of creators(which has the same interface) that can create a concrete type. Now the creation code will search in the dictionary for an int value (resulting from the enum sent from the client) and call the create method, which returns the concrete object via a base-class pointer.
The dictionary can be initialized at one place with the concrete creators corresponding to each possible enum values.
The problem here is that you have to extend this dictionary initialization code when you add a new type of object. A way to avoid is as following.
Let the creator look for a singleton factory instance and register itself in the constructor with the type enums(integers) with which it can create a concret object.
Create a DLL for one/set of creators and have a global instance of the creators.
The name of the DLL can be entered in a config file which is read by the factory in the initialization. The factory loads all the DLLs in this file and this results in the creation of the static objects which registers themselves with the factory.
Now the factory has the map of all the type enums which it can create with the concrete object creators.
The same object creator look-up mechanism is implemented to create the objects.
Now, the factory doesn't need to be extended at all since step 3,4 and 5 doesn't change for new objects introduced. Step 1 can be implemented in one place.
Only thing you need to do is to add a global object for each of the new concrete type which should be there since the C++ doesn't support reflection natively.
kogut, I don't propose this as an answer, but since you ask me to expand on my comment on your original question here's a very brief summary of what the .net environment gives you...
public enum MyEnum
{
[MyAttribute(typeof(ClassNone))]
None,
[MyAttribute(typeof(ClassOne))]
One,
[MyAttribute(typeof(ClassTwo))]
Two,
[MyAttribute(typeof(ClassThree))]
Three
}
So you have your basic enum One, Two, Three etc. which works just like....er....an enum!
But you also code up a class called MyAttribute (and in fact for more information in this area, just search for Attributes). But as you can see this allows you to say, at design time, that such-and-such an enum value is associated with such-and-such a class.
This information is stored in the enum's metadata (the value of a managed environment!) and can be interrogated at runtime (using Reflection). Needless to say this is very powerful, I've used this mechanism to systematically strip out loads of maps of the kind proposed in other answers to your question.
An example of the usefulness is this...at one client I worked with, the convention was to store statuses as strings in a database on the grounds that they would be more readable to a human who needed to run a table query. But this made no sense in the applications, where statuses were pushed through as enums. Take the above approach (with a string rather than a type) and this transform happened on a single line of code as data was read and written. Plus, of course, once you've defined MyAttribute it can be tagged onto any enum you like.
My language if choice these days is c# but this would also be good in (managed) c++.

Possible to instantiate object given its type in C++?

I've been programming in Java way too long, and finding my way back to some C++. I want to write some code that given a class (either a type_info, or its name in a string) can create an instance of that class. For simplicity, let's assume it only needs to call the default constructor. Is this even possible in C++, and if not is it coming in a future TR?
I have found a way to do this, but I'm hoping there is something more "dynamic". For the classes I expect to wish to instantiate (this is a problem in itself, as I want to leave that decision up to configuration), I have created a singleton factory with a statically-created instance that registers itself with another class. eg. for the class Foo, there is also a FooFactory that has a static FooFactory instance, so that at program startup the FooFactory constructor gets called, which registers itself with another class. Then, when I wish to create a Foo at runtime, I find the FooFactory and call it to create the Foo instance. Is there anything better for doing this in C++? I'm guessing I've just been spoiled by rich reflection in Java/C#.
For context, I'm trying to apply some of the IOC container concepts I've become so used to in the Java world to C++, and hoping I can make it as dynamic as possible, without needing to add a Factory class for every other class in my application.
You could always use templates, though I'm not sure that this is what your looking for:
template <typename T>
T
instantiate ()
{
return T ();
}
Or on a class:
template <typename T>
class MyClass
{
...
};
Welcome in C++ :)
You are correct that you will need a Factory to create those objects, however you might not need one Factory per file.
The typical way of going at it is having all instanciable classes derive from a common base class, that we will call Base, so that you'll need a single Factory which will serve a std::unique_ptr<Base> to you each time.
There are 2 ways to implement the Factory:
You can use the Prototype pattern, and register an instance of the class to create, on which a clone function will be called.
You can register a pointer to function or a functor (or std::function<Base*()> in C++0x)
Of course the difficulty is to register those entries dynamically. This is typically done at start-up during static initialization.
// OO-way
class Derived: public Base
{
public:
virtual Derived* clone() const { return new Derived(*this); }
private:
};
// start-up...
namespace { Base* derived = GetFactory().register("Derived", new Derived); }
// ...or in main
int main(int argc, char* argv[])
{
GetFactory().register("Derived", new Derived(argv[1]));
}
// Pointer to function
class Derived: public Base {};
// C++03
namespace {
Base* makeDerived() { return new Derived; }
Base* derived = GetFactory().register("Derived", makeDerived);
}
// C++0x
namespace {
Base* derived = GetFactory().register("Derived", []() { return new Derived; });
}
The main advantage of the start-up way is that you can perfectly define your Derived class in its own file, tuck the registration there, and no other file is impacted by your changes. This is great for handling dependencies.
On the other hand, if the prototype you wish to create requires some external information / parameters, then you are forced to use an initialization method, the simplest of which being to register your instance in main (or equivalent) once you have the necessary parameters.
Quick note: the pointer to function method is the most economic (in memory) and the fastest (in execution), but the syntax is weird...
Regarding the follow-up questions.
Yes it is possible to pass a type to a function, though perhaps not directly:
if the type in question is known at compile time, you can use the templates, though you'll need some time to get acquainted with the syntax
if not, then you'll need to pass some kind of ID and use the factory approach
If you need to pass something akin to object.class then it seems to me that you are approaching the double dispatch use case and it would be worth looking at the Visitor pattern.
No. There is no way to get from a type's name to the actual type; rich reflection is pretty cool, but there's almost always a better way.
no such thing as "var" or "dynamic" in C++ last time I've checked(although that was a WHILE ago). You could use a (void*) pointer and then try casting accordingly. Also, if memory serves me right, C++ does have RTTI which is not reflection but can help with identifying types at runtime.

How can I combine the factory pattern with code flexibility

I am considering a factory function to create different classes in the same hierarchy. I understand that normally a factory is normally implemented as follows:
Person* Person::Create(string type, ...)
{
// Student, Secretary and Professor are all derived classes of Person
if ( type == "student" ) return new Student(...);
if ( type == "secretary" ) return new Secretary(...);
if ( type == "professor" ) return new Professor(...);
return NULL;
}
I am trying to think of a way so that the process can be automated so that the various conditions do not need to be hard-coded.
So far the only way I can think of is using a map and the prototype pattern:
The map will hold the type string in the first element and a class instance (prototype) in the second:
std::map<string, Person> PersonClassMap;
// This may be do-able from a configuration file, I am not sure
PersonClassMap.insert(make_pair("student", Student(...)));
PersonClassMap.insert(make_pair("secondary", Secretary(...)));
PersonClassMap.insert(make_pair("professor", Professor(...)));
The function may look something like this:
Person* Person::Create(string type)
{
map<string, Person>::iterator it = PersonClassMap.find(type) ;
if( it != PersonClassMap.end() )
{
return new Person(it->second); // Use copy constructor to create a new class instance from the prototype.
}
}
Unfortunately, the prototype method only works if you only want the class created by the factory to be identical every time, since it does not support arguments.
Does anybody know if it is possible to do it in a nice way, or am I stuck with the factory function?
I usually build a factory method (or a factory object) when the clients will be providing some information about the object to be created, but they don't know what concrete class the result will be. The determination about how to express the interface to the factory depends completely on what information the clients have. It could be that they provide a string (program text to be parsed, for example), or a set of parameter values (number of dimensions and sizes if we're creating geometric objects in n-space). The factory method then examines the information and decides what kind of object to create or which more specific factory to call.
So the decision about what to build shouldn't be made by the caller; if she knows, then there's no reason for a factory. If the list of things to be built is open-ended, you might even have a registration protocol that allows specific implementations to provide both their construction method and a discriminator function that would allow the factory method to decide which method to call.
It very much depends on what information is necessary and sufficient to decide which kind of object to build.
You can register a factory method (instead of the prebuilt element to copy). This will allow you to call the abstract factory with parameters that are passed to the concrete factory. The limitation here is that the set of parameters of all concrete factories must be the same.
typedef std::string discriminator;
typedef Base* (*creator)( type1, type2, type3 ); // concrete factory, in this case a free function
typedef std::map< discriminator, creator > concrete_map;
class Factory // abstract
{
public:
void register_factory( discriminator d, creator c ) {
factories_[ d ] = c;
}
Base* create( discriminator d, type1 a1, type2 a2, type3 a3 )
{
return (*(factories_[ d ]))( a1, a2, a3 );
}
private:
concrete_map factories_;
};
I have used free function creators to reduce the sample code, but you can define a concrete_factory type and use it instead of the 'creator' element above. Again, as you can see, you are limited to a fixed set of arguments in the factory 'create' method.
Each concrete factory can pass the arguments to the constructor of the given type:
Base* createDerived1( type1 a1, type2 a2, type3 a3 )
{
return new Derived1( a1, a2, a3 );
}
This is more flexible than your approach as you can create instances that hold references to external objects (those can only be initialized during construction) or constant members, or objects that cannot be reset to a different state after construction in a more general wording.
I would add a pure abstract clone method to class Person (which definitely looks like it should be an abstract class, existing mainly for the sake of being subclassed -- if you need a concrete "none of the above" kind of Person it's best done via a separate concrete subclass OtherKindOfPerson, rather than as the base class itself):
virtual Person* clone() const = 0;
and override it in every concrete subclass, e.g. in Student, with a new that invokes the specific concrete subclass's copy ctor:
Person* clone() const { return new Student(*this); }
You also need to change the registry map to:
std::map<string, Person*> PersonClassMap;
[[You could use some smarter pointer than a plain old Person *, but as the map and all of its entries probably needs to survive as long as the process does, this is definitely not a big deal -- the main added value you might get from smarter pointers being smarter behavior upon destruction of the "pointer"!-)]]
Now, your factory function can simply end with:
return it->second->clone();
The changes are needed to avoid the "slicing" effect of using the base class's copy ctor on a subclass that has extra attributes, as well as preserve any virtual method's resolution.
Subclassing a concrete class to yield other concrete classes is a bad idea exactly because these effects can be tricky and a source of bugs (see Haahr's recommendation against it: he writes about Java, but the advice is also good for C++ and other languages [indeed I find his recommendation even more crucial in C++!].
I am not familar with c++ but in many langugaes there concepts of delegates or closures. Means that instead of mapping to instance, you map to the function(delegate, closure) that is responsible to create object.
You could make an enum of each type of person:
enum PersonType { student, secretary, professor };
Well if you want a faster way to do it then using an enum and a switch statement will be many time faster than processing sequential if/else if statements ...
If you look at your two implementations, logically they are identical.
The first implementation is the same as your second if the recursive loop was unrolled. So really there is no advantage of your second implementation.
Regardless what you do you will need to list some where your types mapped to your constructors.
One way of doing it that can be useful is to have this map in a seperate xml file
<person>
<type> student </type>
<constructor> Student </type>
</person>
....
You can then read this xml file in to memory and use reflection to get back your constructor. For the given type. Given that you are using C++ however this will not be that simple as C++ does not have reflection as standard. You will have to look for an extension to provide you with reflection in C++.
But regardless, all these alternatives can not escape what you did in your original implementioan, ie: list the mapping from type to constructor and search the map.

Concrete class specific methods

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.