Home Good way of encapsulating member function pointers for a task manager
Reply: 2

Good way of encapsulating member function pointers for a task manager

dgrat
1#
dgrat Published in 2018-02-12 19:52:56Z

I would like to store a member function pointer and a related object pointer in a template class task. Then I want to make an array and call these member functions in the scheduler class.

However, if I save such tasks in an array, they all need to have the same template type. This obstacle makes saving the object pointer in the task class useless.

Now, I wonder whether there is a smart way to safe an array of such pairs in the related scheduler class. If I use a std::tuple, I cannot iterate at run-time through the container, or at least not in a very readable manner. Is there a solution, where I don't need a template for the scheduler class?

template <class Obj>
struct task {    
    typedef void (Obj::*task_fn_t)();
    Obj*        obj_ptr;  // object referring
    task_fn_t   function; // member function
};

template <class Obj>
class scheduler {
    task <Obj> *_tasks; // problem :(
};
Yakk
2#
Yakk Reply to 2018-02-12 20:51:10Z

Use std::function<void()>.

It can store almost anything that can be invoked with a (). Logically, a Obj*/member function pointer qualifies (with a bit of wrapping).

std::function<void()> f = [obj_ptr]{ obj_ptr->some_member_function(); };

std::function<Sig> is a polymorphic value-type. It stores (by value) anything that can be invoked in a way compaible with Sig. In your case, you want to call something with zero arguments and discard its return value. This is compatible with a void() signature.

On small objects like a member pointer and a object pointer, it will do this without any heap allocation.

For the most part, avoid dynamically allocating a std::function.

template<class..Args>
class scheduler {
  task = std::function<void(Args...)>;
  std::vector<task> tasks;
public:
  void add_task( task t ) { tasks.push_back(std::move(t)); }
  void operator()(Args...args) const {
    for (auto&& t:tasks)
      t(args...);
  }
};

this doesn't let you remove tasks. We can add that ability:

template<class Key, class..Args>
class scheduler {
  task = std::function<void(Args...)>;
  std::map<Key, task> tasks;
public:
  void add_task( Key const& k, task t ) {
    tasks[k] = std::move(t);
  }
  void remove_task( Key const& k ) {
    tasks.erase(k);
  }
  void operator()(Args...args) const {
    for (auto&& t:tasks)
      (t.second)(args...);
  }
};

now you can add tasks with a key, and remove them when you don't want them to run anymore. (Note that they'll be run in the order of the key).

scheduler<std::string> bob;

bob.add_task( "hello", []{std::cout << "hello\n";} );
bob.add_task( "world", []{std::cout << "world\n";} );

bob(); // runs both tasks

bob.remove_task( "world" );

bob(); // runs only the "hello" task
R Sahu
3#
R Sahu Reply to 2018-02-12 20:00:16Z

One way to resolve this is to create a base class for the class template and store pointers to the base class in scheduler.

struct task_base 
{
   virtual ~task_base() {}
   virtual void run() = 0;
};

// scheduler is independent of the class template.
class scheduler {
   std::vector<task_base*> _tasks;
};

template <class Obj>
struct task : task_base {    
    typedef void (Obj::*task_fn_t)();
    Obj*        obj_ptr;  // object referring
    task_fn_t   function; // member function

    virtual void run()
    {
       (obj_ptr->*function)();
    }
};
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.296785 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO