【问题标题】:Container for pointers to member functions with different arguments指向具有不同参数的成员函数的指针的容器
【发布时间】:2012-12-04 13:24:55
【问题描述】:

我到处寻找(现代 C++ 设计和合作),但我找不到存储一组接受不同参数并在不同类上操作的回调的好方法。我需要这个,因为我希望我的应用程序的每个对象都有可能将其方法之一的执行推迟到主 Clock 对象,该对象跟踪当前时间,可以在适当的时候调用此方法。我的目标是这样的代码:

class1void executeAction1Deferred(int time, int arg1, bool arg2)方法中,time是未来想要的执行时间,应该是这样的:

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

Clock::defer(??? what signature ????) 中,代表此任务的对象存储在优先级队列中,其中时间是键。对于每个Clock 量子,然后遍历任务列表,并执行需要在此量子中运行的任务。请注意,我使用“defer”作为静态函数,因为我打算使用单例的 Clock 对象,但它也可以是成员函数,这只是选择问题。

我曾想过使用void* 来保留可变数量的参数,但是让我的action1() 方法接受void* 非常糟糕,因为我每次都需要为参数制作一个结构我直接使用这个函数,没有延迟。

过去我曾多次面临这个问题,但我从未找到真正体面的解决方案。请注意,作为一个小型多平台项目,对于没有经验的程序员可以扩展它的简单性是必不可少的,我不想使用 boost。但是,我们处理的平台的每个编译器都有std::tr1 绑定。问题是:如何定义一个泛型函数的容器,其中的每一个都接受可变数量的参数(最多 N ~ 5 个),并且是不从公共虚拟类派生的对象的不同成员方法?谢谢

【问题讨论】:

  • 为什么要存储采用不同参数集的函数?你想怎么称呼那些?据我了解您的问题,您可以只绑定除一个参数(时间)之外的所有参数 - 然后您的集合将存储所有具有相同签名的函数(需要时间),但在内部可能会包装具有不同签名的函数。
  • 你可以使用variable argument list作为签名,让每个方法处理参数是什么。
  • @aleguna 不抱歉...仅限 tr1

标签: c++ templates callback scheduling generic-programming


【解决方案1】:

使用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::bindstd::mem_fun

【讨论】:

  • 不应该是std::forward&lt;Args...&gt;(args...)吗?另外,这个性能如何——它是否可以与直接调用函数相媲美(假设所有args... 都支持移动语义)?
  • 我觉得我的用法是对的,椭圆会覆盖forward的模板参数。性能与使用虚拟接口class Function { virtual void f() = 0; } 然后手动为每个绑定函数创建子类并使用成员变量存储参数相同。即它将获取参数的副本,然后调用多态函数。
  • 这种方法是否也允许我使用成员函数?顺便说一句,看起来棒极了
  • 是的std::bind 将采用一个成员函数,然后第一个参数是 this 指针。所以class A { void f(B b); }bind(&amp;A::f, this, b) 一样绑定。另见std::mem_fun。 (你可以像c.defer(&amp;A::f, this, ...)一样调用我上面的时钟类
  • @AlexDarsonik:我认为所有这些平台都支持functionbind。您可能必须手动扩展可变参数模板参数功能(即使用 0,1,2...n-1 进行 n 次不同的调用,每个可能的模板参数数量),因为该功能在 ios 或 osx 上可能尚不可用.我认为它在 win 和 android 上。
【解决方案2】:

在 C++11 中,存储 std::function&lt;void()&gt;。您可以使用 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 具有非常相似的 functionbind 模板。我认为它们可能也存在于 TR1 中,尽管我没有任何历史参考资料可供检查。

如果 Boost 真的不是一个选项(并且 TR1 没有提供这些),那么我强烈建议您将其作为一个选项;否则,使用 Boost 作为如何实现它的示例。如果没有可变参数模板,它会变得非常麻烦。

(既然你提到了现代 C++ 设计,请阅读类型列表部分;如果没有可变参数模板,你会这样做)。

【讨论】:

  • 我们正在使用 gcc 4.1.2 进行一些嵌入式开发,它同时具有 std::tr1::functionstd::tr1::bind
  • @BjörnPollex:好的,如果这些存在,那么这可能就是答案。
  • 我需要支持 iOs, osx, win, android, 会对这些编译器做一个快速检查.. 否则我肯定需要为每个平台构建 boost。不使用boost的原因是不想以后被打扰,因为以后会使用Android。谢谢大家
【解决方案3】:

由于您的回调被延迟,包括它们提供的参数,real 签名将是 void(),即 Clock 对象不会自己提供参数,也不需要评估结果。因此,通常您会希望将成员函数指针(或其他函数指针)与所需的参数绑定在一起,并将结果(函数对象)传递给时钟。这就是 boost::bind/std::tr1::bindboost::function/std::function&lt;void()&gt; 出现的地方 - 或 C++11 lambda:

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

【讨论】:

  • 看来我可以将自己限制为 TR1(绑定 + 函数),对吧?
猜你喜欢
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-29
  • 1970-01-01
  • 2013-05-22
  • 2011-04-03
相关资源
最近更新 更多