Передать содержимое кортежа в качестве аргументов переменной функции - PullRequest
0 голосов
/ 05 ноября 2010

Я какое-то время играю с C ++ 0x, и теперь я хочу использовать переменные шаблоны и кортежи для реализации класса «Задача».Я собираюсь передать объекты Task во вновь созданные потоки (используя pthread).Класс задачи будет содержать указатель на функцию, которая должна вызываться внутри потока, и аргументы для этой функции, упрощенный код:

class TaskCaller
{
    // ...
    virtual bool dispatch (void);
};

template<typename ...T_arguments> Task :
    public TaskCaller
{
    public:
        // ...
        Task (bool           (*function) (T_arguments&...),
              T_arguments... arguments) :
                  function_arguments_tuple (arguments...),
                  function (function)
        {
            // ...
        }

        bool dispatch (void)
        {
            return TupleUnpack<sizeof ...(T_arguments)>::unpack (this->function, this->function_arguments_tuple);
        }

    private:
        std::tuple<T_arguments&...> function_arguments_tuple;
        bool                        (*function) (T_arguments...);
};

и код, который я использую для распаковки кортежа в аргументы функции:

template<unsigned int i>  class TupleUnpack 
{
    public:
        template<typename T_return_type, typename ...T_tuple_arguments, typename ...T_function_arguments>
            inline static T_return_type unpack (T_return_type                     (*function) (T_tuple_arguments&...), 
                                                std::tuple<T_tuple_arguments...>& arguments_tuple,
                                                T_function_arguments              ...function_arguments)
            {
                return TupleUnpack<i-1>::unpack (function, arguments_tuple, std::get<i-1> (arguments_tuple), function_arguments...);
            }                       
};

template<> class TupleUnpack<0> 
{
    public:
        template<typename T_return_type, typename ...T_tuple_arguments, typename ...T_function_arguments>
            inline static T_return_type unpack (T_return_type                     (*function) (T_tuple_arguments&...), 
                                                std::tuple<T_tuple_arguments...>& arguments_tuple,
                                                T_function_arguments              ...function_arguments)
            {
                return function (function_arguments...);
            }          
};

Вариант использования:

bool task_function (Foo &foo, Bar &bar)
{
    // ...
    return true;
}

void* thread_function (void* argument)
{
    Task* task ((Task*) argument);

    task->dispatch ();

    delete task;

    pthread_exit (0);
}

void function (void)
{
    Foo             foo (1, 2, 3);
    Bar             bar (1, 2, 3);
    Task<Foo, Bar>* task = new Task (task_function, std::move (foo) std::move (bar));
    pthread_t       thread_id;

    pthread_create (&thread_id, task_function, task);
}

Я еще не тестировал этот код, но это всего лишь идея.

Теперь мне интересно, как класс TupleUnpack повлияет на окончательный код.Насколько мне известно, окончательная реализация функции Task :: dispatch (после шаблонов синтаксического анализа) будет эквивалентна:

template<typename ...T_arguments> static bool Task<...T_arguments>::dispatch (void)
{
    return this->function (std::get<0> (this->function_arguments_tuple), std::get<1> (this->function_arguments_tuple), ..., std::get<n> (this->function_arguments_tuple));
}

верно?

Более того, сам кортеж и std :: get () должны «исчезнуть» в конечном коде и не предоставлять никаких затрат времени выполнения (согласно документации Boost) .

Может бытьесть лучший способ решить мою проблему ...

1 Ответ

3 голосов
/ 08 ноября 2010

Это должно быть эквивалентным, но единственный способ убедиться в этом - это проверить с помощью используемого вами компилятора.

Обратите внимание, что вместо него можно использовать std::function с std::bindНапример, что-то вроде:

template<typename ...T_arguments> class Task : public TaskCaller
{
    std::function<bool (T_arguments&...)> functor;
public:
    Task (bool (*func)(T_arguments&...), T_arguments... arguments)
      : functor(std::bind(func, arguments...))
    {}
    bool dispatch() {
        return functor();
    }
    // ...

Или еще лучше: пусть пользователь передает std::function in:

class Task : public TaskCaller {
    std::function<bool ()> functor;
public:
    Task(std::function<bool ()> func) : functor(func) {}
    // ...

, который позволяет пользователю выбирать, что передавать, а не заставлять егоиспользовать свободные функции или статические функции-члены.

...