Чистый способ хранения функции и ее аргументов (произвольного типа, произвольного числа)

Для библиотеки я бы хотел, чтобы функция принимала другую функцию и ее аргументы, а затем сохраняла их все для последующего вызова. Аргументы должны учитывать любую смесь типов, но функции должны возвращать только void. Что-то вроде этого:

void myFunc1(int arg1, float arg2);
void myFunc2(const char *arg1);
class DelayedCaller
{ ...
public:
static DelayedCaller *setup(Function func, …);
};

...
DelayedCaller* caller1 = DelayedCaller::setup(&myFunc1, 123, 45.6);
DelayedCaller* caller2 = DelayedCaller::setup(&myFunc2, "A string");

caller1->call(); // Calls myFunc1(), with arguments 123 and 45.6
caller2->call(); // Calls myFunc2(), with argument "A string"

Один из подходов — заставить DelayedCaller :: setup () принять функцию std ::, и пользователи моей библиотеки должны использовать std :: bind () перед вызовом setup (). Однако есть ли способ реализовать setup () так, чтобы пользователям не нужно было делать привязку самостоятельно?

редактировать: DelayedCaller — это существующий класс. setup () — это новый статический метод, который я хотел бы добавить.

5

Решение

Возможность использования шаблонов с переменными значениями и вызова std::bind() изнутри setup() функция:

#include <iostream>
#include <string>
#include <functional>
#include <memory>

void myFunc1(int arg1, float arg2)
{
std::cout << arg1 << ", " << arg2 << '\n';
}
void myFunc2(const char *arg1)
{
std::cout << arg1 << '\n';
}

class DelayedCaller
{
public:
template <typename TFunction, typename... TArgs>
static std::unique_ptr<DelayedCaller> setup(TFunction&& a_func,
TArgs&&... a_args)
{
return std::unique_ptr<DelayedCaller>(new DelayedCaller(
std::bind(std::forward<TFunction>(a_func),
std::forward<TArgs>(a_args)...)));
}
void call() const { func_(); }

private:
using func_type = std::function<void()>;
DelayedCaller(func_type&& a_ft) : func_(std::forward<func_type>(a_ft)) {}
func_type func_;
};

int main()
{
auto caller1(DelayedCaller::setup(&myFunc1, 123, 45.6));
auto caller2(DelayedCaller::setup(&myFunc2, "A string"));

caller1->call();
caller2->call();

return 0;
}

Выход:

123, 45,6
Строка

Вернуть умный указатель, такой как std::unique_ptr, вместо возврата необработанного указателя (или возврата по значению и избежания динамического выделения. func_type является подвижным, если аргументы являются подвижными, или копирование может быть довольно дешевым. Вам может понадобиться определить конструктор перемещения и оператор присваивания перемещения, они генерируются при определенных условиях).

7

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

Вы можете использовать лямбда-функции, чтобы скрыть привязки:

#include <functional>

class DelayedCaller : public std::function< void(void) > {
public:
DelayedCaller(std::function< void(void) > fn)
: std::function< void(void) >(fn) {}
};

DelayedCaller caller1([]() { myFunc1(123, 45.6); });
DelayedCaller caller2([]() { myFunc2("A string"); });

caller1(); // Calls myFunc1(), with arguments 123 and 45.6
caller2(); // Calls myFunc2(), with argument "A string"

Это также дает пользователям вашей библиотеки больше гибкости. Они не ограничены одним вызовом функции, и функции имеют доступ к исходной среде, в которой они были созданы:

int x;

DelayedCaller caller3 = [&x]() {
if (x == 0)
DoSomething();
else
DoSomethingElse();
};
7

Если ваша единственная задача состоит в том, чтобы скрыть привязку аргумента от места вызова при сохранении интерфейса, используйте переменные шаблоны

class DelayedCaller
{
public:
template<typename... Args>
static DelayedCaller* setup(void (functionPtr*)(Args...), Args&&... args)
{
return new DelayedCaller(std::bind(functionPtr, std::forward<Args>(args)...));
}

DelayedCaller(const std::function<void()>& f) : f(f) {}

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

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

0

Если вы хотите / можете использовать C ++ 11 будущее библиотека, которую вы можете использовать std::async

#include <future>

auto caller = std::async(myFunc1, 123, 45.6); // Creates a future object.

caller.get(); // Waits for the function to get executed and returns result.

Чтобы заставить ленивую оценку использовать:

auto caller = std::async(std::launch::deferred, myFunc1, 123, 45.6);

Также это имеет то преимущество, что вызов функции может выполняться в другом потоке, который использует многоядерное оборудование. Однако это может не подходить в каждом случае.

0
По вопросам рекламы [email protected]