tags:

views:

637

answers:

4

Is there a method/pattern/library to do something like that (in pseudo-code):

task_queue.push_back(ObjectType object1, method1);
task_queue.push_back(OtherObjectType object2, method2);

so that I could do the something like:

for(int i=0; i<task_queue.size(); i++) {
    task_queue[i].object -> method();
}

so that it would call:

obj1.method1();
obj2.method2();

Or is that an impossible dream?

And if there's a way to add a number of parameters to call - that would be the best.

Doug T. please see this Excellent answer!

Dave Van den Eynde's version works well too.

+2  A: 

Yes you would want to combine boost::bind and boost::functions its very powerful stuff.

This version now compiles, thanks to Slava!

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>

class CClass1
{
public:
    void AMethod(int i, float f) { std::cout << "CClass1::AMethod(" << i <<");\n"; }
};

class CClass2
{
public:
    void AnotherMethod(int i) { std::cout << "CClass2::AnotherMethod(" << i <<");\n"; }
};

int main() {
    boost::function< void (int) > method1, method2;
    CClass1 class1instance;
    CClass2 class2instance;
    method1 = boost::bind(&CClass1::AMethod, class1instance, _1, 6.0) ;
    method2 = boost::bind(&CClass2::AnotherMethod, class2instance, _1) ;

    // does class1instance.AMethod(5, 6.0)
    method1(5);

    // does class2instance.AMethod(5)
    method2(5);


    // stored in a vector of functions...
    std::vector< boost::function<void(int)> > functionVec;
    functionVec.push_back(method1);
    functionVec.push_back(method2);

    for ( int i = 0; i < functionVec.size(); ++i)
    {         
      functionVec[i]( 5);
    };
    return 0;
};
Doug T.
Awesome, but what if I have CClass1 AND CClass2 and ideally I'd like to store them both?
Slava N
Also I think there should be _method =_ boost::bind(AMethod,....
Slava N
Nit: bind copies its arguments by default, so the comment "does instance.AMethod(...)" should read "Does *copy of* instance.AMethod()"To really call instance, boost::ref() (or cref) should be used to wrap the instance passed to bind().
Éric Malenfant
Hmm, this produces a lot of "missing type specifier - int assumed. Note: ...." and "'method1' : redefinition; different basic types" see declaration of 'method1'. If you could point out how to solve that - that'd be perfect. (searching now)
Slava N
Slava N
copy-pasted your version in, thanks!
Doug T.
Great! Thank you for the effort!
Slava N
This is awesome. can I have your blog or twitter where I can follow what you are upto? want to learn more stuff like this. :) .thanks a lot for sharing knowledge time.
Gollum
A: 

Maybe you could think in another way:

for(int i=0; i<task_queue.size(); i++) {
    task_queue[i].method(task_queue[i].object);
}
Jérôme
This means that the task_queue method() must be overloaded for every type of object, and so task queue must know about every type of object. So to add a new type of object, task_queue must be changed, which is bad.
KeithB
No, here task_queue[i] is an instance of the Object, not of the task_queue. So task_queue[i].method() call the method which is defined in the object. You do not overwrite any method in task_queue!
Jérôme
+1  A: 

Since C++ does not support heterogenous containers, your objects will have to have a shared base (so you can get away with having a container for pointers to this base class).

class shared_base {
     public:
     virtual void method() = 0; // force subclasses to do something about it
};

typedef std::list<shared_base*> obj_list;

class object : public shared_base {
     public:
     virtual void method() { methodx(); }
     private:
     int methodx(); 
};

// ...
list.insert(new object);

// ...
std::for_each(list.begin(), list.end(), std::mem_fun(&shared_base::method));

Are you trying to implement the Hollywood principle otherwise known as Inversion-of-Control (and also, poorman's error handling)?

Look up both the Observer and Visitor patterns -- they might be of interest.

dirkgently
+1  A: 

I whipped something up.

#include <vector>
#include <algorithm>
#include <iostream>

template <typename ARG>
class TaskSystem
{
private:
    class DelegateBase
    {
    public:
        virtual ~DelegateBase() { }
        virtual void Invoke(ARG arg) = 0;
    };

    template <typename T>
    class Delegate : public DelegateBase
    {
    public:
     typedef void (T::*Func)(ARG arg);

    private:
        Func m_func;
        T* m_object;

    public:
        Delegate(T* object, Func func)
            : m_object(object), m_func(func)
        { }

        virtual void Invoke(ARG arg) 
     { 
      ((*m_object).*(m_func))(arg);
     }
    };

    typedef std::vector<DelegateBase*> Delegates;
    Delegates m_delegates;

public:
    ~TaskSystem()
    {
        Clear();
    }

    void Clear()
    {
     Delegates::iterator item = m_delegates.begin();

     for (; item != m_delegates.end(); ++item)
     {
            delete *item;
     }

        m_delegates.clear();
    }

    template <typename T>
    void AddDelegate(T& object, typename Delegate<T>::Func func)
    {
     DelegateBase* delegate = new Delegate<T>(&object, func);
     m_delegates.push_back(delegate);
    }

    void Invoke(ARG arg)
    {
     Delegates::iterator item = m_delegates.begin();

     for (; item != m_delegates.end(); ++item)
     {
      (*item)->Invoke(arg);
     }
    }

};

class TaskObject1
{
public:
    void CallOne(const wchar_t* value)
    {
        std::wcout << L"CallOne(): " << value << std::endl;
    }

    void CallTwo(const wchar_t* value)
    {
        std::wcout << L"CallTwo(): " << value << std::endl;
    }
};

class TaskObject2
{
public:
    void CallThree(const wchar_t* value)
    {
        std::wcout << L"CallThree(): " << value << std::endl;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    TaskSystem<const wchar_t*> tasks;

    TaskObject1 obj1;
    TaskObject2 obj2;

    tasks.AddDelegate(obj1, &TaskObject1::CallOne);
    tasks.AddDelegate(obj1, &TaskObject1::CallTwo);
    tasks.AddDelegate(obj2, &TaskObject2::CallThree);

    tasks.Invoke(L"Hello, World!\n");

    return 0;
}
Dave Van den Eynde
Wow! It works too, thanks! However your templating skill break my brain :) Awesome!
Slava N
Well the templating isn't really hard. It's the member function pointer syntax that boggled me when I first did this.
Dave Van den Eynde