Sunday, May 1, 2011

How to maintain a list of functions in C++/STL ?

Hi,

Before asking you my question directly, I'm going to describe the nature of my prolem. I'm coding a 2D simulation using C++/OpenGL with the GLFW library. And I need to manage a lot of threads properly. In GLFW we have to call the function: thread = glfwCreateThread(ThreadFunc, NULL); (the first parameter is the function that'll execute the thread, and the second represents the parameters of this function). And glfwCreateThread, has to be called every time! (ie: in each cycle). This way of working, doesn't really help me, because it breaks the way i'm building my code because i need to create threads out of the main loop scope. So I'm creating a ThreadManager class, that'll have the following prototype :

class ThreadManager {

  public:
         ThreadManager();
         void AddThread(void*, void GLFWCALL (*pt2Func)(void*)); 
         void DeleteThread(void GLFWCALL (*pt2Func)(void*));
         void ExecuteAllThreads();

  private:
         vector<void GLFWCALL (*pt2Func)(void*)> list_functions;
         // some attributs             


};

So for example, if I want to add a specific thread I'll just need to call AddThread with the specific parameters, and the specific function. And the goal is just to be able to call: ExecuteAllThreads(); inside the main loop scope. But for this i need to have something like:

void ExecuteAllThreads() {

      vector<void GLFWCALL (*pt2Func)(void*)>::const_iterator iter_end = list_functions.end();
      for(vector<void GLFWCALL (*pt2Func)(void*)>::const_iterator iter = list_functions.begin();
      iter != iter_end; ++iter) {

           thread = glfwCreateThread(&(iter*), param);
      }
}

And inside AddThread, I'll just have to add the function referenced by the pt2Func to the vector : list_functions.

Alright, this is the general idea of what i want to do.. is it the right way to go ? You have a better idea ? How to do this, really ? (I mean the problem is the syntax, i'm not sure how to do this).

Thank you !

From stackoverflow
  • What about trying to store them using boost::function ?

    They could simulate your specific functions, since they behave like real objects but in fact are simple functors.

    iain : +1 For boost::function. This is the ideal way to store function pointers and/or function objects with the same signature. This will also mean that additional arguments can be passed with boost::bind with the function object is created.
  • You need to create threads in each simulation cycle? That sounds suspicious. Create your threads once, and reuse them.

    Thread creation isn't a cheap operation. You definitely don't want to do that in every iteration step.

    If possible, I'd recommend you use Boost.Thread for threads instead, to give you type safety and other handy features. Threading is complicated enough without throwing away type safety and working against a primitive C API.

    That said, what you're asking is possible, although it gets messy. First, you need to store the arguments for the functions as well, so your class looks something like this:

    class ThreadManager {
    
      public:
             typedef void GLFWCALL (*pt2Func)(void*); // Just a convenience typedef
             typedef std::vector<std::pair<pt2Func, void*> > func_vector;
             ThreadManager();
             void AddThread(void*, pt2Func); 
             void DeleteThread(pt2Func);
             void ExecuteAllThreads();
    
      private:
             func_vector list_functions;
    };
    

    And then ExecuteAllThreads:

    void ExecuteAllThreads() {
    
          func_vector::const_iterator iter_end = list_functions.end();
    
          for(func_vector::const_iterator iter = list_functions.begin();
          iter != iter_end; ++iter) {
    
               thread = glfwCreateThread(iter->first, iter->second);
          }
    }
    

    And of course inside AddThread you'd have to add a pair of function pointer and argument to the vector.

    Note that Boost.Thread would solve most of this a lot cleaner, since it expects a thread to be a functor (which can hold state, and therefore doesn't need explicit arguments).

    Your thread function could be defined something like this:

    class MyThread {
      MyThread(/* Pass whatever arguments you want in the constructor, and store them in the object as members */);
    
      void operator()() {
        // The actual thread function
      }
    
    };
    

    And since the operator() doesn't take any parameters, it becomes a lot simpler to start the thread.

    Amokrane : Thank you for this snippet ! This is exactly what I wanted to do.. but you are right: 1/I find it suspicious too, but it seems to work like this :/ 2/ I'm going to take a look at the boost library, i'm not familiar with it but as the threads represent a crucial point of my simulation, I think it's worth learning it!
  • I am not familiar with the threading system you use. So bear with me.

    Shouldn't you maintain a list of thread identifiers?

     class ThreadManager {
         private:
           vector<thread_id_t> mThreads;
         // ...
     };
    

    and then in ExecuteAllThreads you'd do:

     for_each(mThreads.begin(), mThreads.end(), bind(some_fun, _1));
    

    (using Boost Lambda bind and placeholder arguments) where some_fun is the function you call for all threads.

    Or is it that you want to call a set of functions for a given thread?

    Amokrane : I don't know what does exactly the bind function of the boost library.. the idea of maintaining a list of threads identifiers is good but i don't think it can be applied to glfw system threading anyway..will think about that.. thanks!
  • Consider Boost Thread and Thread Group

    Amokrane : thank's.. will do!

0 comments:

Post a Comment