使用参数 c++11 绑定通用回调

Posted

技术标签:

【中文标题】使用参数 c++11 绑定通用回调【英文标题】:Binding generic callbacks with arguments 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? 在我看来,您正试图将成员函数指针转换为不可能的函数指针。例如,请参阅***.com/questions/4296281/…。 @Holt 它们是函数指针,但我正在考虑将它们设为 std::function 。我正在尝试你的答案。 @Holt 感谢您的回答,到目前为止它对我有所帮助,但请查看更新后的问题。再次感谢! @Belfer4 你确定你有drawCall = std::bind(drawCall, ...);吗? drawCall 之一不应该以不同的方式命名吗?我真的不明白你想在这里做什么...... 【参考方案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 pointers 或std::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 上提供一个最小示例,我将能够给您更具体的答案。

【讨论】:

以上是关于使用参数 c++11 绑定通用回调的主要内容,如果未能解决你的问题,请参考以下文章

在宜搭中使用动作绑定和获取回调函数参数!

回调函数是啥意思 啥是回调函数

返回绑定参数的函数对象?

C++ 通用回调实现

通用类型 'ComponentRef<C>' 需要 1 个类型参数

从本机 c 代码 (JNI) 为 Java 中的回调函数传递多个参数