繁体   English   中英

C 回调函数的 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}};

对于此解决方案,我需要使用单独的工具/脚本生成标头。

另一种解决方案是使用一些预处理器魔法(BoostPP),比如

BOOST_PP_FOR((0,1024), PRED, OP, MYSTERIOUS_CALLBACK_MACRO);

根据我的经验,这些宏对于开发人员来说是不可读的,并且难以维护。

理想情况下,我可以使用类似RegisterCallback(popt, [n](void*){/*n-th callback*/});

但是 lambda 函数是一个函子而不是函数指针。

我的问题是:我可以动态创建这些函数吗? 或者有没有比上面两个更好的解决方案?

谢谢你。

编辑

我从 Botje 那里得到了答案。 将对象传递给函数回调需要您访问二进制级别的代码(超出 C/C++ 级别)。 如果您可以允许,那么 libffi 可以是一个解决方案,因为它会生成一个指向您的函数的每个实例的特定指针。 它得到了广泛的支持,但是,例如 Visual Studio Compiler 不在列表中。

编辑 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 或调用语义的细节; 如果它是公开可用的软件,则指向实际文档的链接会有所帮助。

无论如何,我发现这个问题很有趣,并将在下面展示一个解决方案,该解决方案利用我们可以使用模板生成不同函数的事实,而不是手动编码它们。

下面的代码假设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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM