tags:

views:

176

answers:

8

Hi, 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 ...

A: 

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.

Hassan Syed
A: 

Boost MPL is one way.

See http://stackoverflow.com/questions/1185464

Tim Sylvester
MPL is quite overkill, and take a person has to be a sadist to learn it (I am one).
Hassan Syed
That depends on what he's trying to do. The OP's example is clearly a trivial one to give us the idea, his actual need might be complex or long-lived enough enough to justify a good, general solution like MPL.
Tim Sylvester
+3  A: 

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)
xtofl
wow ... I'm kinda of newbie in C++, don't really get what's struct IBaseType, nor "std::transform" line ... :(
migajek
The IBaseType is my version of your `Abstract`. The `std::transform` is just show-off: it calls `create` on each factory in `factories` and puts the result in the back of the `objects` vector. Mighty handy sometimes, but no-use in here, really :)
xtofl
A: 

Factory pattern could work (see e.g. http://en.wikipedia.org/wiki/Factory%5Fmethod%5Fpattern). 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.

Matt Gordon
+1  A: 

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();
Jon Benedicto
A: 

Looks like you need some functors -- function objects. You could have a parent functor and then specialize them.
Here is an example:

struct Functor
{
  virtual void operator()(/* parameter list */) = 0;
};

struct Specialized_Functor
:  public Functor
{
  void operator() (/* parameter list */)
  {
    // do stuff
  }
};

// ...
typedef boost::shared_ptr<Functor> Ptr_Functor;
typedef std::list<Ptr_Functor>     Functor_Container;
//...
Functor_Container my_functors;
Specialized_Functor any_functor;
//...
my_functors.push_back(any_functor);
//...
Function_Container::iterator iter;
for (iter =  my_functors.begin();
     iter != my_functors.end();
     ++iter)
{
  Functor * p_functor = *iter; // For clarity.
  (p_functor)(); // p_functor->operator()
}

My understanding of the essence of your question is handling methods and putting the methods into a container.

Another solution is to use pointers to methods; however, one needs and instance of an object to execute a pointer to a non-static method of a class.

Thomas Matthews
+2  A: 

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!
Igor Oks
+1  A: 

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 );
KitsuneYMG
I believe your code might be something more flexible or whatever, but it's just too compilcated for me, and requires Boost for compilation - which I'd like to avoid... but thanks, probably one day I'll appreciate it :) :)
migajek
@michal I understand. MPL can be very hard to grok and even harder 6 months after you write it. The Boost.PP stuff is, of course, pure evil because it relies on the preprocessor and people have an, imo, irrational hatred of macros.
KitsuneYMG
I just did a bcp on the preprocessor module and it is entirely self contained (at least bcp thinks so). You you could just use that part of boost if the whole is too much.
KitsuneYMG