mardi 4 octobre 2016

Call std::function in different thread

I'm working in a c++ framework, mostly written before c++11, that allows us to fire events from a thread to a different thread (assuming that the receiving thread is running an event queue, so it's mainly used to fire events to the main UI thread from a helper thread).

Currently, the code to accomplish this is very verbose - it requires us to define two classes:

  1. An event class that contains any information we want to transfer.
  2. A listener class that handles the event in the receiving thread.

We've recently moved to c++11/14 and have been working on updating a lot of our code to use smart pointers, standard containers, and lambdas. I'd like to write a generic class that allows me to send a lambda to be run in a different thread. Something like:

mBoundary = make_unique<ThreadBoundary>( []( int value ) { doSomething( value ); } ); 
mBoundary->callInMainThread( 47 );

ThreadBoundary boundary2( []( std::string value ) { displayString( value ); } );
boundary2.callInMainThreadWait( "Show this string to the user" );

As an initial attempt, I've currently got this working with a lambda that doesn't take any parameters, built on top of the current framework functionality (omitting error checking, cleanup, etc):

class ThreadBoundary
{
public:
    ThreadBoundary( std::function<void()> function ):mFunction( function )
    {
        mListener = make_shared<ThreadBoundaryListener>();
        cApplication::addThreadBoundaryListener( mListener );
    }

    void callInMainThread()
    {
        cApplication::fireEventInMainThread( new ThreadBoundaryEvent( mFunction ) );
    }

    class ThreadBoundaryEvent:public FrameworkThreadBoundaryEvent
    {
    public:
        ThreadBoundaryEvent( std::function<void()> function )
        {
            mFunction = function;
        }

        void call() { mFunction(); }

    private:
        std::function<void()> mFunction;
    };

    class ThreadBoundaryListener:public FrameworkThreadBoundaryListener
    {
    public:
        ThreadBoundaryListener() {}

        void handleEvent( const FrameworkThreadBoundaryEvent* event )
        {
            dynamic_cast<const ThreadBoundaryEvent*>( event )->call();
        }
    };

private:
    shared_ptr<ThreadBoundaryListener> mListener;
    std::function<void()> mFunction;
};

This allows me to fire "one-off" events to the main thread, however, without being able to send along parameters it's functionality is pretty limited.

I'd like to make this class use variadic templates so that I can pass anything to the callInMainThread function. However, I haven't been able to figure out how to store the parameter pack in the event and pass it along to the function inside call(). So my questions are:

  1. Is it possible to store a parameter pack and later pass it to a std::function.
  2. Is there a better way to do this in c++11/14 that doesn't require a huge redesign? Our framework is currently wrapping OS functionality to accomplish this (i.e. on Windows it uses SendMessage vs performSelectorOnMainThread in OS X).

Aucun commentaire:

Enregistrer un commentaire