C++ "Dynamic" C 回调函数的函数指针
C++ "Dynamic" function pointers for C callback functions
我有一个 API 用于管理相机配置。有 344 个单独的选项需要管理。当某个值改变时API调用回调函数通知程序。注册函数采用
void RegisterCallback(Option * ptr, void (fn*)(void*))
作为回调函数的函数指针。 我不能使用单一函数进行回调,因为我现在做回调来自哪里。
一个解决方案是创建 344 个单独的回调函数:
void callback0(void*);
void callback1(void*);
...
void callback{n-1}(void*);
static void(*)(void*) callbacks[] = {callback0, callback1, ..., callback{n-1}};
对于此解决方案,我需要使用单独的 tool/script 生成 header。
另一种解决方案是使用一些预处理器魔法 (BoostPP),例如
BOOST_PP_FOR((0,1024), PRED, OP, MYSTERIOUS_CALLBACK_MACRO);
根据我的经验,这些宏对于开发人员来说是不可读的,并且难以维护。
理想情况下我可以使用类似
RegisterCallback(popt, [n](void*){/*n-th callback*/});
但是 lambda 函数是仿函数而不是函数指针。
我的问题是:我可以动态创建这些函数吗?或者对于这个问题有没有比上面两个更好的解决方案?
谢谢。
编辑
我已经从 Botje 那里得到了答复。将 object 传递给函数回调需要您访问二进制级别的代码(超出 C/C++ 的级别)。如果你可以允许,那么 libffi 可以是一个解决方案,因为它生成一个指向你函数的每个实例的特定指针。它得到广泛支持,但是,例如 Visual Studio 编译器不在列表中。
编辑 2
正如其他人指出的那样,它也应该与 VS 一起工作。
您可以使用 libffi 动态生成闭包。这些是常规函数指针,可以保留一个指向用户数据的指针,在本例中为回调号码。
#include <iostream>
#include <vector>
#include "ffi.h"
using namespace std;
void the_callback(size_t num, void* arg) {
cout << "This is callback #" << num << " with argument " << hex << arg << endl;
}
void generic_cb(ffi_cif *cif, void *ret, void **args, void *user_data) {
the_callback((size_t) user_data, *(void **)args[0]);
}
typedef void (*Callback)(void*);
ffi_cif callback_cif;
int main() {
ffi_type *argtypes[] = { &ffi_type_pointer };
ffi_status st = ffi_prep_cif(&callback_cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, argtypes);
std::vector<Callback> callbacks;
for (size_t i = 0; i < 100; i++) {
void *cb;
ffi_closure *writable = (ffi_closure*)ffi_closure_alloc(sizeof(ffi_closure), &cb);
st = ffi_prep_closure_loc(writable, &callback_cif, generic_cb, (void*)i, cb);
callbacks.push_back((Callback)cb);
}
callbacks[13]((void*)0xabcdef);
callbacks[87]((void*)0x1234);
}
这会产生以下输出:
This is callback #13 with argument 0xabcdef
This is callback #57 with argument 0x1234
您没有提供很多关于相机的细节 API 或调用的语义;如果它是公开可用的软件,link 到实际文档会很有帮助。
无论如何,我发现这个问题很有趣,并将在下面展示一个解决方案,该解决方案利用了这样一个事实,即我们可以使用模板生成不同的函数,而不是手动编写代码。
下面的代码假定void RegisterCallback(Option *ptr, void (fn*)(void*))
中的Option *ptr
参数是你从相机获取的指针;它用作相机 API 您要为其注册此回调的选项的指示器。可以想象,相机 API 有一个包含 344 个成员的枚举,这将取代下面的 size_t
模板参数和地图键类型。
调用了一个简单的递归模板函数来为 344 个选项中的每个选项注册一个回调。
#include <cstdio>
#include <map>
#include <cassert>
using namespace std;
typedef void (*voidFnPtrT)(void *);
////////////////////////////////////////////////////////////
// This part simulates the camera API.
class Camera
{ public:
static constexpr size_t NUM_OPTS = 344;
static constexpr size_t OPTSIZE = 100;
struct Option
{
unsigned char data[OPTSIZE];
};
Option &GetOption(size_t index) { return options[index]; }
void RegisterCallback(Option *opt, voidFnPtrT callback)
{
callbacks[opt] = callback;
}
/// Set a new option value and call the callback,
/// if set for that option.
void SetOptionVal(size_t index, Option *newVal)
{
assert(index < NUM_OPTS);
options[index] = *newVal;
OnOptionChange(index);
}
private:
Option options[NUM_OPTS];
map<Option *, voidFnPtrT> callbacks;
/// If there is a callback registered
/// with the option at that index, call it.
void OnOptionChange(size_t index)
{
auto iter = callbacks.find(options + index);
if(iter != callbacks.end())
{
(*iter->second)(iter->first);
}
}
};
//////////// End API simulation ///////////////////////////////
/// A single user function which can serve as a
/// callback for any property change. The property is
/// identified through the index (could be an enum
/// or any other compile time constant).
void singlebigcallback(size_t index, void *data)
{
// This is still awkward, but in some location we must code
// what happens for each option. In terms of lines of code
// it is not much advantageous to simply putting each switch
// case contents directly into a specialization of callbackTemplFn.
// In terms of maintainability that may actually be even better.
// Anyway, you wanted a single function, here it is.
switch(index)
{
// obviously this demo code does not need a switch but can
// be handled by assembling a string, but imagine some distinct
// activity in each case.
case 0: printf("reacting to change in property 0\n"); break;
case 1: printf("reacting to change in property 1\n"); break;
case 2: printf("reacting to change in property 2\n"); break;
default: printf("property callback for %zu not yet implemented\n", index); break;
}
}
/// A template with a number template parameter.
/// The signature of each instantiation is
/// void ()(void *) and hence can be used
/// as a callback function for the camera.
template<size_t N> void callbackTemplFn(void *data)
{
singlebigcallback(N, data);
}
/// This function registers the proper callbackTemplFn
/// for the given N and then recursively calls itself
/// with N-1.
template<size_t N> void RegisterAllCallbacks(Camera &cam)
{
cam.RegisterCallback(&cam.GetOption(N), &callbackTemplFn<N>);
RegisterAllCallbacks<N-1>(cam);
}
/// The recursion end: There is no index smaller than 0,
/// so we register the proper function and return.
template<> void RegisterAllCallbacks<0>(Camera &cam)
{
cam.RegisterCallback(&cam.GetOption(0), &callbackTemplFn<0>);
// No further recursion.
}
int main()
{
Camera camera;
Camera::Option opt; // Normally one would put some data in there.
RegisterAllCallbacks<Camera::NUM_OPTS-1>(camera);
camera.SetOptionVal(0, &opt);
camera.SetOptionVal(2, &opt);
camera.SetOptionVal(Camera::NUM_OPTS-1, &opt);
return 0;
}
示例会话:
$ g++ -Wall -o callbacks callbacks.cpp && ./callbacks
reacting to change in property 0
reacting to change in property 2
property callback for 343 not yet implemented
我有一个 API 用于管理相机配置。有 344 个单独的选项需要管理。当某个值改变时API调用回调函数通知程序。注册函数采用
void RegisterCallback(Option * ptr, void (fn*)(void*))
作为回调函数的函数指针。 我不能使用单一函数进行回调,因为我现在做回调来自哪里。
一个解决方案是创建 344 个单独的回调函数:
void callback0(void*);
void callback1(void*);
...
void callback{n-1}(void*);
static void(*)(void*) callbacks[] = {callback0, callback1, ..., callback{n-1}};
对于此解决方案,我需要使用单独的 tool/script 生成 header。
另一种解决方案是使用一些预处理器魔法 (BoostPP),例如
BOOST_PP_FOR((0,1024), PRED, OP, MYSTERIOUS_CALLBACK_MACRO);
根据我的经验,这些宏对于开发人员来说是不可读的,并且难以维护。
理想情况下我可以使用类似
RegisterCallback(popt, [n](void*){/*n-th callback*/});
但是 lambda 函数是仿函数而不是函数指针。
我的问题是:我可以动态创建这些函数吗?或者对于这个问题有没有比上面两个更好的解决方案?
谢谢。
编辑
我已经从 Botje 那里得到了答复。将 object 传递给函数回调需要您访问二进制级别的代码(超出 C/C++ 的级别)。如果你可以允许,那么 libffi 可以是一个解决方案,因为它生成一个指向你函数的每个实例的特定指针。它得到广泛支持,但是,例如 Visual Studio 编译器不在列表中。
编辑 2 正如其他人指出的那样,它也应该与 VS 一起工作。
您可以使用 libffi 动态生成闭包。这些是常规函数指针,可以保留一个指向用户数据的指针,在本例中为回调号码。
#include <iostream>
#include <vector>
#include "ffi.h"
using namespace std;
void the_callback(size_t num, void* arg) {
cout << "This is callback #" << num << " with argument " << hex << arg << endl;
}
void generic_cb(ffi_cif *cif, void *ret, void **args, void *user_data) {
the_callback((size_t) user_data, *(void **)args[0]);
}
typedef void (*Callback)(void*);
ffi_cif callback_cif;
int main() {
ffi_type *argtypes[] = { &ffi_type_pointer };
ffi_status st = ffi_prep_cif(&callback_cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, argtypes);
std::vector<Callback> callbacks;
for (size_t i = 0; i < 100; i++) {
void *cb;
ffi_closure *writable = (ffi_closure*)ffi_closure_alloc(sizeof(ffi_closure), &cb);
st = ffi_prep_closure_loc(writable, &callback_cif, generic_cb, (void*)i, cb);
callbacks.push_back((Callback)cb);
}
callbacks[13]((void*)0xabcdef);
callbacks[87]((void*)0x1234);
}
这会产生以下输出:
This is callback #13 with argument 0xabcdef
This is callback #57 with argument 0x1234
您没有提供很多关于相机的细节 API 或调用的语义;如果它是公开可用的软件,link 到实际文档会很有帮助。
无论如何,我发现这个问题很有趣,并将在下面展示一个解决方案,该解决方案利用了这样一个事实,即我们可以使用模板生成不同的函数,而不是手动编写代码。
下面的代码假定void RegisterCallback(Option *ptr, void (fn*)(void*))
中的Option *ptr
参数是你从相机获取的指针;它用作相机 API 您要为其注册此回调的选项的指示器。可以想象,相机 API 有一个包含 344 个成员的枚举,这将取代下面的 size_t
模板参数和地图键类型。
调用了一个简单的递归模板函数来为 344 个选项中的每个选项注册一个回调。
#include <cstdio>
#include <map>
#include <cassert>
using namespace std;
typedef void (*voidFnPtrT)(void *);
////////////////////////////////////////////////////////////
// This part simulates the camera API.
class Camera
{ public:
static constexpr size_t NUM_OPTS = 344;
static constexpr size_t OPTSIZE = 100;
struct Option
{
unsigned char data[OPTSIZE];
};
Option &GetOption(size_t index) { return options[index]; }
void RegisterCallback(Option *opt, voidFnPtrT callback)
{
callbacks[opt] = callback;
}
/// Set a new option value and call the callback,
/// if set for that option.
void SetOptionVal(size_t index, Option *newVal)
{
assert(index < NUM_OPTS);
options[index] = *newVal;
OnOptionChange(index);
}
private:
Option options[NUM_OPTS];
map<Option *, voidFnPtrT> callbacks;
/// If there is a callback registered
/// with the option at that index, call it.
void OnOptionChange(size_t index)
{
auto iter = callbacks.find(options + index);
if(iter != callbacks.end())
{
(*iter->second)(iter->first);
}
}
};
//////////// End API simulation ///////////////////////////////
/// A single user function which can serve as a
/// callback for any property change. The property is
/// identified through the index (could be an enum
/// or any other compile time constant).
void singlebigcallback(size_t index, void *data)
{
// This is still awkward, but in some location we must code
// what happens for each option. In terms of lines of code
// it is not much advantageous to simply putting each switch
// case contents directly into a specialization of callbackTemplFn.
// In terms of maintainability that may actually be even better.
// Anyway, you wanted a single function, here it is.
switch(index)
{
// obviously this demo code does not need a switch but can
// be handled by assembling a string, but imagine some distinct
// activity in each case.
case 0: printf("reacting to change in property 0\n"); break;
case 1: printf("reacting to change in property 1\n"); break;
case 2: printf("reacting to change in property 2\n"); break;
default: printf("property callback for %zu not yet implemented\n", index); break;
}
}
/// A template with a number template parameter.
/// The signature of each instantiation is
/// void ()(void *) and hence can be used
/// as a callback function for the camera.
template<size_t N> void callbackTemplFn(void *data)
{
singlebigcallback(N, data);
}
/// This function registers the proper callbackTemplFn
/// for the given N and then recursively calls itself
/// with N-1.
template<size_t N> void RegisterAllCallbacks(Camera &cam)
{
cam.RegisterCallback(&cam.GetOption(N), &callbackTemplFn<N>);
RegisterAllCallbacks<N-1>(cam);
}
/// The recursion end: There is no index smaller than 0,
/// so we register the proper function and return.
template<> void RegisterAllCallbacks<0>(Camera &cam)
{
cam.RegisterCallback(&cam.GetOption(0), &callbackTemplFn<0>);
// No further recursion.
}
int main()
{
Camera camera;
Camera::Option opt; // Normally one would put some data in there.
RegisterAllCallbacks<Camera::NUM_OPTS-1>(camera);
camera.SetOptionVal(0, &opt);
camera.SetOptionVal(2, &opt);
camera.SetOptionVal(Camera::NUM_OPTS-1, &opt);
return 0;
}
示例会话:
$ g++ -Wall -o callbacks callbacks.cpp && ./callbacks
reacting to change in property 0
reacting to change in property 2
property callback for 343 not yet implemented