【问题标题】:A clean way to store a function and its (arbitrary-type, arbitrary-number) arguments一种存储函数及其(任意类型、任意数量)参数的简洁方法
【发布时间】:2013-02-12 12:57:36
【问题描述】:

对于一个库,我想要一个函数来接受另一个函数及其参数,然后将它们全部存储起来以供以后调用。参数必须允许任何类型的混合,但函数只需要返回 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::function,并让我的库用户在调用 setup() 之前使用 std::bind()。但是,有没有办法实现 setup() 让用户不需要自己进行绑定?

编辑:DelayedCaller 是一个现有的类。 setup() 是我想添加的一个新的静态方法。

【问题讨论】:

  • 您可以将 std::async 与惰性求值一起使用。它返回一个 std::future 对象,用于存储任务以供以后使用。

标签: c++ c++11 variadic


【解决方案1】:

一种可能性是使用可变参数模板并从setup() 函数中调用std::bind()

#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 是可移动的,或者复制起来可能很便宜无论如何。您可能需要定义移动构造函数和移动赋值运算符,它们是在某些条件下生成的)。

【讨论】:

  • +1 表示unique_ptr 和模板函数类型。我忘记了那个陷阱。
  • 这是完美的;谢谢。非常感谢指针安全和移动语义的详细集成。
【解决方案2】:

您可以使用 lambda 函数来隐藏绑定:

#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();
};

【讨论】:

  • 这当然是我推荐使用std::async的路线。
  • 我没有在我的问题中提到它,但是 DelayedCaller 需要是一个类,因为它负责我图书馆中的其他事情。不过,感谢您跳出框框思考!
  • @JKSH - 好的,我把它变成了一门课。
  • @Ferruccio - 是的,那个构造函数可以工作。不过,我为我的问题选择了 hmjd 的答案,因为生成的 API 与我的原始代码相匹配。感谢您的努力。
【解决方案3】:

如果您唯一关心的是在保留界面的同时隐藏调用站点的参数绑定,请使用可变参数模板

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;
};

公共构造函数仍然为您的用户提供了使用 lambda 对其进行初始化的可能性。

【讨论】:

  • 是的,简单的界面是我的目标。我选择 hmjd 的答案是因为它具有额外的鲁棒性,但这个简约的例子非常适合像我这样的初学者;感谢分享。
【解决方案4】:

如果你想/能够使用 C++11 future 库,你可以使用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);

这还有一个优点,即函数调用可以在使用多核硬件的不同线程上执行。但是,这可能并不适用于所有情况。

【讨论】:

  • 这不适合我的情况,因为我正在扩展自定义线程管理 API :) 不过,感谢您演示 std::async() 的使用,这对我来说是新的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-13
  • 2018-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多