Для библиотеки я бы хотел, чтобы функция принимала другую функцию и ее аргументы, а затем сохраняла их все для последующего вызова. Аргументы должны учитывать любую смесь типов, но функции должны возвращать только 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 () — это новый статический метод, который я хотел бы добавить.
Возможность использования шаблонов с переменными значениями и вызова 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
является подвижным, если аргументы являются подвижными, или копирование может быть довольно дешевым. Вам может понадобиться определить конструктор перемещения и оператор присваивания перемещения, они генерируются при определенных условиях).
Вы можете использовать лямбда-функции, чтобы скрыть привязки:
#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();
};
Если ваша единственная задача состоит в том, чтобы скрыть привязку аргумента от места вызова при сохранении интерфейса, используйте переменные шаблоны
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;
};
Публичный конструктор все еще предлагает вашим пользователям возможность инициализировать его лямбда-выражением, если они этого пожелают.
Если вы хотите / можете использовать 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);
Также это имеет то преимущество, что вызов функции может выполняться в другом потоке, который использует многоядерное оборудование. Однако это может не подходить в каждом случае.