简体   繁体   中英

Storing and Executing Functions From A Class (C++)

I have a class called "Tasks" that needs to store methods from other classes, and be able to execute them. I'd like it to work like this:

Window *window = new Window();
Tasks* tasks = new Tasks();
tasks.m_tasks.Add(window.Create("My Window"));

Then I could call that window creation from my tasks class, by iterating over the stored tasks and executing each one:

tasks.ExecuteTasks();

What would be the datastructure of "m_tasks" that stores the functions, and how could I call them?

I would use a std::list<std::function<void()> > , or boost::function if std::function is not available.

And you'll need to change the syntax of that Add call to avoid executing the Create method right away.

C++11:

class Tasks {
public:
    void Add(const std::function<void()>& f)
    { callbacks_.push_back( f ); }

    void Add(std::function<void()>&& f)
    { callbacks_.emplace_back( std::move( f ) ); }
    // ...
private:
    std::list<std::function<void()> > callbacks_;
};

int main() {
    Window window;
    // ...
    tasks.Add( [&]() { window.Create("My Window"); } );
    // ...
}

C++03:

class Tasks {
public:
    void Add(const boost::function<void()>& f)
    { callbacks_.push_back( f ); }

private:
    std::list<boost::function<void()> > callbacks_;
};

int main() {
    // ...
    tasks.Add( boost::bind( &Window::Create, boost::ref(window), "My Window" ) );
    // ...
}

You could use a list of tr1 or boost ::functions as @aschepler says, but this scenario is perfect for boost::signals .

class Tasks {
    boost::signal<void ()> m_tasks;
};

// ...
tasks.m_tasks.connect(&someFunction);

// ExecuteTasks:
tasks.m_tasks();

This allows for a lot of extra functionality, like handling arguments, returns, and letting clients disconnect their tasks if they want to.

You'll need slightly complicated structure for this:

class Task
{
public:
   virtual void Execute()=0;
};
template<class T, class R, class P1>
class Function1 : public Task
{
public:
   Function1(T *ptr, R (T::*fptr)(P1), P1 p1) : ptr(ptr), fptr(fptr),p1(p1) { }
   void Execute() { (ptr->*fptr)(p1); }
private:
   T *ptr;
   R (T::*fptr)(P1);
   P1 p1;
};
std::vector<Task*> vec;

This is relatively straightforward if you know what the arguments will be. You could use a function pointer, with some extras to make it a 'method' pointer. See:

http://mdzahidh.wordpress.com/2008/07/16/pointer-to-c-class-methods-or-should-you-call-em-method-pointers/

However, this would not allow you to pass arbitrary arguments. You might be able to do it with C++ templates, but it would be nasty hackery. I would strongly advise avoiding this and going with traditional function/method pointers if at all possible.

m_tasks is going to be a collection of some sort, I'd probably use a list unless you need to be able to add/remove in the middle. The thing you will be storing in the list is a function pointer. That is, a pointer to a function. With the straightforward version of the code I have below, you cannot have generic function pointers, you must be specific about the parameter types and the return value type. It might be possible to use templates to break this restriction. I don't know off the top of my head.

// define FunctionPtr as a pointer to a function that takes a single char* param and returns void 
typedef void(*FunctionPtr)(char*);
// define an stl:list of FunctionPtr items
std:list<FunctionPtr> m_tasks;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM