【问题标题】:Binding generic callbacks with arguments c++11使用参数 c++11 绑定通用回调
【发布时间】:2017-01-06 13:12:07
【问题描述】:

我有一个名为 Renderer 的类,它包含两个回调对象;

void (*drawCall)(const sf::Drawable& drawable, const sf::RenderStates& states);
void (*drawPrimCall)(const sf::Vertex* vertices, unsigned int vertexCount,
    sf::PrimitiveType type, const sf::RenderStates& states);

我想要一个绑定这些回调的公共函数,它是模板化的,需要一个有两个draw方法的类,每个方法都有它们的有效参数,如下所示:

template<typename T>
inline void setRenderer(T* instance)
{
    using namespace std::placeholders;
    drawCall = std::bind(&T::draw, instance, _1, _2);
    drawPrimCall = std::bind(&T::draw, instance, _1, _2, _3, _4);
}

但是,正如您可能想象的那样,这不起作用。我仍在学习 std::bind 并且我不确定占位符的使用,它理解我在这里尝试做什么。

我尝试在 Google 中寻找答案,但有点令人生畏,感谢您的帮助!

更新:

好的,@Holt 给出的答案有效,更改为 std::function 后,我的回调如下所示:

std::function<void(const sf::Drawable& drawable, const sf::RenderStates& states)> drawCall_fn;
std::function<void(const sf::Vertex* vertices, unsigned int vertexCount,
                   sf::PrimitiveType type, const sf::RenderStates& states)> drawPrimCall_fn;

现在对于 setRenderer 方法,由于外部库的限制,我还需要像这样传递函数引用;

template<typename T>
inline void setRenderer(T* instance, void(T::*drawCall)(const sf::Drawable&, const sf::RenderStates&))
{
    using namespace std::placeholders;
    drawCall_fn = std::bind(drawCall, instance, _1, _2);
}

这样当被调用时我可以这样做:

renderer.setRenderer<sf::RenderTarget>(&window, &sf::RenderTarget::draw);

**注意:我现在只处理 drawCall_fn,因为之后我可以轻松展开。

UPDATE2:为 setRenderer drawCall 添加了缺失的参数。

【问题讨论】:

  • drawCalldrawPrimCall有哪些类型? std::function?
  • 在我看来,您正试图将成员函数指针转换为不可能的函数指针。例如,请参阅stackoverflow.com/questions/4296281/…
  • @Holt 它们是函数指针,但我正在考虑将它们设为 std::function 。我正在尝试你的答案。
  • @Holt 感谢您的回答,到目前为止它对我有所帮助,但请查看更新后的问题。再次感谢!
  • @Belfer4 你确定你有drawCall = std::bind(drawCall, ...);吗? drawCall 之一不应该以不同的方式命名吗?我真的不明白你想在这里做什么......

标签: c++ c++11 generics callback


【解决方案1】:

您无法使用函数指针做您想做的事,因为您需要“捕获”(使用捕获 lambda 或使用 std::bindinstance。你应该使用std::function:

std::function<void(const sf::Drawable&, const sf::RenderStates&)> drawCall;
std::function<void(const sf::Vertex*, unsigned int,
    sf::PrimitiveType, const sf::RenderStates&)> drawPrimCall;

然后,您可以使用 lambda 或强制 &amp;T::draw 的重载解析来分配它们:

drawCall = [instance](const sf::Drawable& drawable, const sf::RenderStates& states) {
    instance->draw(drawable, states);
});

// Or:
drawCall = std::bind((void (T::*)(const sf::Drawable&, const sf::RenderStates&))&T::draw, 
                     instance, _1, _2);

在这两种情况下,您都必须重新指定参数列表,但您可以使用一个小的辅助函数来避免这种情况:

template <typename T, typename R, typename... Args, typename... BArgs>
void gbind_mem(std::function<R(Args...)> &fn, R(T::*mfn)(Args...), 
               T *instance, BArgs&&... args) {
    fn = std::bind(mfn, instance, std::forward<BArgs>(args)...);
}

然后您可以使用以下方法轻松绑定:

gbind_mem(drawCall, &T::draw, instance, _1, _2);

您让编译器完成从drawCall 推断参数的艰巨工作。

【讨论】:

    【解决方案2】:

    ...拥有两个回调对象

    C++ 中不存在“回调对象”。您可能正在寻找的是function pointersstd::function。在函数指针的情况下,您的语法不正确-您需要添加*

    void (*drawCall)(const sf::Drawable& drawable, const sf::RenderStates& states = sf::RenderStates::Default);
    void (*drawPrimCall)(const sf::Vertex* vertices, unsigned int vertexCount, sf::PrimitiveType type,
                        const sf::RenderStates& states = sf::RenderStates::Default);
    

    然后您就可以设置它们了。示例:

    void (*callback)(int); 
    void real_function(int) { }
    
    int main()
    {
        callback = &real_function;
    }
    

    如果你的“真实函数”签名与回调的不匹配,你应该使用无捕获lambda expressions,它可以隐式转换为函数指针,来适应它:

    void (*callback)(int); 
    void real_function(int, float) { }
    
    int main()
    {
        callback = [](int x){ return real_function(x, 5.f); };
    }
    

    如果您在 wandbox 上提供一个最小示例,我将能够给您更具体的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-27
      • 2014-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多