c++ - 在没有上下文参数的情况下将有状态的 lambda 传递给 C 风格的函数
问题描述
我正在尝试将 C 库集成到我的 C++ 项目中。C 库具有将函数指针作为参数的函数,但这些函数指针被编写为 typedef。
typedef void(*FileHandler_t)(File* handle);
然后是一个注册回调的函数,如下所示:
void RegisterCallback(FileHandler_t handler);
我可以创建一个 lambda 表达式并将其传递给参数处理程序的 RegisterCallback
auto handler = [](File* handle){ //handle cb };
这很好用。
RegisterCallback(handler);
但是当我尝试传递要在处理程序内部使用的局部变量时,我收到编译器错误。
auto handler = [&container](File* handle){ //handle cb };
现在 RegisterCallback 不再编译。我想这样做是因为我不想使用全局变量。无论如何,这些场景中正式使用的错误是否存在?
据我所知,除了修改库本身之外,没有其他办法。
解决方案
通常,使用此类回调的 C 库的设计者会提供一些将状态变量与回调相关联的方法。经常void*
吵架。一些库在您传递回调时采用该状态指针,并将指针传递给回调函数,例如 WinAPI 通常会这样做,请参阅EnumWindows。其他库允许将该东西放入它们传递给回调的某个对象中,例如 libpng 使用png_set_write_fn
APIpng_get_io_ptr
函数来实现。
如果在阅读了可用的文档后,您得出结论认为您的图书馆并非如此,并且您不能或不想向图书馆作者寻求支持,这意味着您必须更具创造力。
一种解决方法是使用哈希映射将文件与容器相关联。像这样:
static std::unordered_map<File*, Container*> s_containers;
之前将文件与容器关联RegisterCallback
,并在回调中通过文件指针查找容器。想想线程,也许你需要用互斥锁来保护那个静态哈希映射。还要考虑异常处理,也许您需要 RAII 类在构造函数中注册/在析构函数中注销。
另一种更简单但更有限的方法是使用 C++/11 中引入的 thread_local 存储类说明符,声明
static thread_local Container* s_container;
并在回调中使用它。如果您的库确实阻塞了 IO 并且没有在内部使用线程,那么这很有可能可以正常工作。但是您仍然需要处理错误,即将全局变量重置为nullptr
容器超出范围时。
更新:如果您可以更改库,那么这样做比两种解决方法都要好得多。再向 RegisterCallback传递一个void*
参数,并将处理程序更改为typedef void(*FileHandler_t)(File* handle, void* context);
如果您从 C++ 使用库,通常最好将回调实现为私有静态方法,并将this
指针传递到库中。这将允许您在回调中调用实例方法,同时保持类的内部状态隐藏。
推荐阅读
- go - golang写常用函数的好习惯
- java - 安装到另一个 Tomcat 服务器 8.5.35 后的 java.lang.NoClassDefFoundError
- angular - 将角度组件注入 chrome 扩展内容脚本
- python - for 循环顶部的项目与其中的项目不匹配,但仍会打印
- python - 如何使用 Flask-Login 在 Flask 中模拟和测试多用户登录/注销?
- python - 信用卡功能错误,python:“列表”对象没有属性键
- java - java:InputStream实例的read vs readNBytes
- javascript - SVG 动画 beginElement() 不适用于 endElement()
- javascript - 当输入区域有焦点时将新类分配给标签
- shell - 有人可以解释 .bashrc 和 .bash_profile 之间的区别吗?