c++ - 在 C 中模拟 std::bind

标签 c++ c c++11 function-pointers

我使用 std::bind 提供回调,同时通过首先绑定(bind)一些参数来抽象一些逻辑。即

void start() {

    int secret_id = 43534;

    //Bind the secret_id to the callback function object
    std::function<void(std::string)> cb = std::bind(&callback, secret_id, std::placeholders::_1);

    do_action(cb);

}

void do_action(std::function<void(std::string)> cb) {

    std::string result = "hello world";
    //Do some things...

    //Call the callback
    cb(result);
}

void callback(int secret_id, std::string result) {

    //Callback can now do something with the result and secret_id

}

所以在上面的例子中,do_action 不需要知道 secret_id 并且其他函数可以在没有自己的 secret_id 的情况下重用它。这在 do_action 是某种异步操作时特别有用。

我的问题是,有没有办法仅使用 C 将参数值绑定(bind)到函数指针?

如果不是通过模拟 std::bind 那么是否有另一种方法可以将数据从 first() 传递到 callback() 而不会使中性的 do_action() 复杂化?

最佳答案

没有。 C 不允许您直接这样做。

在 C 中,处理回调的标准方法是使用上下文指针:

void register_callback(void (*cback)(void *context, int data),
                       void *context);

这意味着除了回调应该处理的正常参数(在上述情况下是一个整数)之外,您还将传递一个接受 void * 的函数,并且您还将传递一个 void * 你想被传回。

这个 void * 通常指向一个 struct ,它将包含你在回调中需要的所有额外参数或数据,并且使用这种方法库不依赖于这个上下文是什么。如果回调不需要任何上下文,您只需将 NULL 指针作为 context 传递,并在从库中调用时忽略第一个参数。

如果上下文是适合 void * 大小的简单数据(例如整数)并且您的环境是不会有问题你可以通过传递一个伪造的 void * 来欺骗库分配上下文并管理其生命周期)。

关于如何欺骗语言来避免这种限制(仍然留在可移植 C 的土地上)我可以想到一些 hack:

首先我们分配一个双参数回调和上下文数据池

void (*cbf[6])(int, int);
int ctx[6];

然后我们编写(或宏生成)我们希望注册的函数,该函数将调用双参数版本。

void call_with_0(int x) { cbf[0](ctx[0], x); }
void call_with_1(int x) { cbf[1](ctx[1], x); }
void call_with_2(int x) { cbf[2](ctx[2], x); }
void call_with_3(int x) { cbf[3](ctx[3], x); }
void call_with_4(int x) { cbf[4](ctx[4], x); }
void call_with_5(int x) { cbf[5](ctx[5], x); }

我们还将它们存储在分配和释放它们的池中:

int first_free_cback = 0;
int next_free_cback[6] = {1, 2, 3, 4, 5, -1};

void (*cbacks[6])(int) = { call_with_0,
                           call_with_1,
                           call_with_2,
                           call_with_3,
                           call_with_4,
                           call_with_5 };

然后为了绑定(bind)第一个参数,我们可以做类似的事情

void (*bind(void (*g)(int, int), int v0))(int)
{
    if (first_free_cback == -1) return NULL;
    int i = first_free_cback;
    first_free_cback = next_free_cback[i];
    cbf[i] = g; ctx[i] = v0;
    return cbacks[i];
}

但绑定(bind)函数也必须显式释放

int deallocate_bound_cback(void (*f)(int))
{
    for (int i=0; i<6; i++) {
        if (f == cbacks[i]) {
            next_free_cback[i] = first_free_cback;
            first_free_cback = i;
            return 1;
        }
    }
    return 0;
}

关于c++ - 在 C 中模拟 std::bind,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19046028/

相关文章:

c++ - 引用类型的 std::optional 特化

c++ - 在什么情况下 operator= 应该用左值/右值重载而不是 copy-and-swap 来实现?

c++ - 从 R 调用 C++ 函数并集成它时出错

c - 指针赋值 - uint16_t

c - 为什么在反转主函数参数位置时编译仍然有效

c++ - 在 C/C++ 中将两个二维数组初始化为零

c++ - 使用 integer_sequence 过滤元组

c++ - 混合 C++ 和 Fortran 链接问题

c++ - QTableView - 放置指针(突出显示选择到列表的第一行

objective-c - 在另一个函数中声明的 NSMutableArray 中添加对象