Reputation: 8614
I have quite a lot of classes declared, all of them are inheriting from a base (kind of abstract) class ... so all of them have common methods I'd like to use ...
now, I need to have a list of classes (not objects), later make instance of them in a loop and use the instances for calling mentioned common methods ...
the pseudo-code
class Abstract {
void Something();
}
class TaskOne : public Abstract {
void Something(); // method implemented somewhere below
}
class TaskTwo : public Abstract {
void Something(); // method implemented somewhere below
}
...
list<Abstract> lst;
lst.push_back(TaskOne); // passing class type, not instance!
lst.push_back(TaskTwo);
Abstract tmpObject = new lst[0]; //I know its wrong, just a way of expressing what I'd like to do to have instance of TaskOne!
please give any tips ...
Upvotes: 3
Views: 5953
Reputation: 41509
You could create a templated factory object:
struct IFactory { virtual IBaseType* create() = 0; };
template< typename Type > struct Factory : public IFactory {
virtual Type* create( ) {
return new Type( );
}
};
struct IBaseType { /* common methods */ virtual ~IBaseType(){} };
IFactory* factories[] = {
new Factory<SubType1>
, new Factory<SubType2>
// ...
};
std::vector<IBaseType*> objects;
objects.push_back( factories[1]->create() ); // and another object!
// don't forget to delete the entries in the
// vector before clearing it (leak leak)
Upvotes: 4
Reputation: 12901
Boost.MPL is the answer here. Don't listen to Hassan Syed.
Example:
namespace mpl=boost::mpl;
typedef mpl::vector< CLASS1, CLASS2,...,CLASSN > class_list_a;
typedef mpl::push_back< class_list_a ANOTHER_CLASS>::type class_list_b;
typedef mpl::push_back<
typename mpl::push_back<
class_list_b,
ANOTHER_TYPE_1>::type,
ANOTHER_TYPE_2>::type
class_list;
struct functor {
template<class U> void operator(U& u) {
u.something();
}
};
...
// in some function
boost::mpl::for_each<class_list>( functor() );
EDIT: BOOST_PP could also work
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#define MY_CLASSES (TYPE_1)(TYPE_2)(TYPE_N)
#define MY_FUNCTION(r, data, elem) elem() . data;
#define CALL_SOMETHING_ON(_CLASSES_, _ARGS_) \
BOOST_PP_SEQ_FOR_EACH( MY_FUNCTION, someThing( _ARGS_ ), _CLASSES_ )
int foo = 1;
CALL_SOMETHING_ON( MY_CLASSES, foo )
running cpp foo.c
yields:
int foo = 1;
TYPE_1() . someThing( foo ); \
TYPE_2() . someThing( foo ); \
TYPE_N() . someThing( foo );
Upvotes: 2
Reputation: 27250
I would go with the templated factory, like xtofl proposed, but simplify its usage
struct IFactory { virtual IBaseType* create() = 0; };
template< typename Type > struct Factory : public IFactory {
virtual Type* create( ) {
return new Type( );
}
};
list<IFactory*> lst;
lst.push_back(new Factory<TaskOne>);
lst.push_back(new Factory<TaskTwo>);
Abstract *tmpObject = lst[0]->create();
// don't forget to delete all the factory instances!
Upvotes: 3
Reputation: 10582
I'd suggest using a factory-based design. Your list stores a bunch of factory instances, and you can create an Abstract-derived instance by calling the right factory method.
For example:
class Abstract
{
virtual void Something() = 0;
};
class TaskOne : public Abstract
{
void Something();
};
class AbstractFactory
{
public:
Abstract* CreateInstance();
};
template <class T> class Factory : public AbstractFactory
{
public:
Abstract* CreateInstance()
{
return new T();
}
};
...
std::vector<AbstractFactory*> Factories;
Factories.push_back(new Factory<TaskOne>());
...
Abstract *Instance = Factories[ 0 ]->CreateInstance();
Upvotes: 1
Reputation: 20485
You need to have have a list of pointers of type abstract. initialize all the pointers to null and construct to the correct class later. You don't need to pre-type the pointers before hand. You can just rely on the RTTI latter.
If you do need to pre-type them before hand than just add an enum
to Abstract
for the types. This saves you memory if the inherited classes are large. But you have to maintain the enum of types yourself, not the most elegant way of doing things.
If you have to initialize and pre-type them, just initialize the classes using the default constructor. But this way you are using memory when not needed. RTTI will be your friend here in this case.
Upvotes: 0
Reputation: 51
Factory pattern could work (see e.g. http://en.wikipedia.org/wiki/Factory_method_pattern). Something along the lines of:
#define stringify(a) #a
static int hashstr(const char* s) {/*hash fn of choice*/}
list<int> types;
lst.push_back(hashtr(stringify(TaskOne))); // passing class type, not instance!
lst.push_back(hashtr(stringify(TaskTwo)));
static Abstract* Instance(int classid)
{
switch(id)
{
case hashstr(stringify(TaskOne)):
return new TaskOne;
//etc
}
}
You can be considerably more elegant with extra work. Embedding class id's into the class declaration as static ints is often a good start.
Upvotes: 0