使用参数 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 添加了缺失的参数。
【问题讨论】:
drawCall
和drawPrimCall
有哪些类型? std::function
?
在我看来,您正试图将成员函数指针转换为不可能的函数指针。例如,请参阅***.com/questions/4296281/…。
@Holt 它们是函数指针,但我正在考虑将它们设为 std::function 。我正在尝试你的答案。
@Holt 感谢您的回答,到目前为止它对我有所帮助,但请查看更新后的问题。再次感谢!
@Belfer4 你确定你有drawCall = std::bind(drawCall, ...);
吗? drawCall
之一不应该以不同的方式命名吗?我真的不明白你想在这里做什么......
【参考方案1】:
您无法使用函数指针做您想做的事,因为您需要“捕获”(使用捕获 lambda 或使用 std::bind
)instance
。你应该使用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 或强制 &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 绑定通用回调的主要内容,如果未能解决你的问题,请参考以下文章