Контейнер для указателей на функции-члены с разными аргументами

Я смотрю везде (современный дизайн C ++ & со) но я не могу найти хороший способ сохранить набор обратных вызовов, которые принимают разные аргументы и работают с разными классами. Мне это нужно, потому что я хотел бы, чтобы каждый объект моего приложения имел возможность отложить выполнение одного из его методов на главном Clock объект, который, отслеживая текущее время, может вызвать эти методы в нужный момент. Код, к которому я стремлюсь, выглядит примерно так:

в void executeAction1Deferred(int time, int arg1, bool arg2) метод class1где время — это время выполнения, требуемое в будущем, должно быть что-то вроде этого:

Clock::defer(time, Class1::action1, this, arg1, arg2);

В Clock::defer(??? what signature ????) объект, который представляет эту задачу, хранится в очереди приоритетов, где время является ключом. Для каждого Clock В этом случае список заданий просматривается, и задачи, которые необходимо выполнить в этом кванте, будут выполнены. Обратите внимание, что я использовал «defer» в качестве статической функции, потому что я намерен Clock объект одиночного объекта, но он также может быть функцией-членом, это просто вопрос выбора.

Я думал об использовании void* сохранить переменное количество аргументов, но имея мой action1() метод принятия void* это довольно ужасно, еще и потому, что мне нужно было бы создавать структуру аргумента каждый раз, когда я использую эту функцию напрямую, не откладывая ее.

В прошлом я неоднократно сталкивался с этой проблемой и никогда не находил действительно достойного решения. Обратите внимание, что, поскольку это небольшой мультиплатформенный проект, в котором очень важна простота сборки для неопытных программистов, которые могут его расширить, я не хочу использовать boost. Но каждый компилятор для платформ, к которым мы обращаемся, имеет std::tr1 привязывать. Вопрос заключается в следующем: как определить контейнер универсальных функций, каждая из которых принимает переменное число параметров (до N ~ 5) и является другим методом-членом объектов, которые не являются производными от общего виртуального класса? Спасибо

0

Решение

использование std::function<void()> чтобы сохранить вызовы, а затем использовать переменный аргумент шаблона для пересылки и связывания параметров функции:

class Clock
{
vector<function<void()>> tasks;

template<class F, class... Args>
void defer(F f, Args&&... args)
{
tasks.push_back(bind(f, forward<Args>(args)...);
}

}

void f(A a, B b);

int main()
{
A a;
B b;

Clock c;
c.push_back(f, a, b);
}

смотрите также std::bind а также std::mem_fun

7

Другие решения

В C ++ 11 магазин std::function<void()>, Ты можешь использовать std::bind создать функцию из одной другой подписи, например:

std::vector<std::function<void()>> deferred;
deferred.push_back(std::bind(&Class1::action1, this, arg1, arg2));

// later...
for (auto f : deferred) f();

Если C ++ 11 не вариант, то Boost очень похож function а также bind шаблоны. Я думаю, что они также могут существовать в TR1, хотя у меня нет исторических ссылок для проверки.

Если Boost действительно не подходит (а TR1 не предоставляет их), я настоятельно рекомендую вам сделать это; в противном случае используйте Boost в качестве примера того, как это реализовать. Без вариадических шаблонов он становится очень волосатым.

(А так как вы упомянули Modern C ++ Design, прочитайте раздел о списках типов; именно так вы бы поступили без вариабельных шаблонов).

2

Поскольку ваши обратные вызовы откладываются, включая предоставленные аргументы, реальный подпись будет void(), т.е. объект Clock не будет предоставлять аргументы сам по себе и не нуждается в оценке результатов. Поэтому, как правило, вы захотите связать указатели на функции-члены (или другие указатели на функции) вместе с необходимыми аргументами и передать результат (объект функции) в часы. Это где boost::bind/std::tr1::bind а также boost::function/std::function<void()> входите — или лямбды C ++ 11:

Clock::defer(time, boost::bind(&Class1::action1, this, arg1, arg2));
//C++11 lambda:
Clock::defer(time, [=](){action1(arg1, arg2);} );

Но то, что вы делаете, уже сделано — взгляните на таймеры Boost.Asio:

boost::asio::basic_deadline_timer timer(ioService, expiryTime);
timer.async_wait([=](){action1(arg1, arg2);} //perform action1 when the timer is done
1
По вопросам рекламы [email protected]