Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C中模拟std::bind_C++_C_C++11_Function Pointers - Fatal编程技术网

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

C++ 在C中模拟std::bind,c++,c,c++11,function-pointers,C++,C,C++11,Function Pointers,我使用std::bind提供回调,同时通过首先绑定一些参数来抽象一些逻辑。i、 e 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);

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

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

}
void start(){
int secret_id=43534;
//将secret_id绑定到回调函数对象
std::function cb=std::bind(&callback,secret_id,std::占位符::_1);
行动(cb);
}
无效do_动作(标准::函数cb){
std::string result=“hello world”;
//做一些事情。。。
//打回电话
cb(结果);
}
无效回调(int secret\u id,std::string result){
//回调现在可以对结果和机密id执行操作
}
因此,在上面的示例中,do_操作不需要知道secret_id,其他函数可以重用它,而不需要自己的secret_id。当do_操作是某种异步操作时,这尤其有用

我的问题是,是否有一种仅使用C将参数值绑定到函数指针的方法


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

否。C不允许直接这样做

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

void register_callback(void (*cback)(void *context, int data),
                       void *context);
这意味着您将传递一个函数,该函数除了回调应该处理的正常参数(在上述情况下为整数)之外,还将接受一个
void*
,并且您还将传递一个希望传回的
void*

void*
通常指向一个
struct
,该结构将包含回调中所需的所有额外参数或数据,使用此方法库不依赖于此上下文。如果回调不需要任何上下文,只需将空指针作为
context
传递,并在从库中调用时忽略第一个参数

有些东西有点粗俗,形式上不安全,但有时会这样做,如果上下文是符合
void*
大小的简单数据(例如整数)如果您的环境不会出现问题,您可以通过传递一个假的
void*
来欺骗库,该假
void*
只是一个整数,然后在从库中调用时将其转换回整数(这样可以避免调用方分配上下文和管理其生存期)

关于如何欺骗语言以避免这种限制(仍停留在可移植C的领域),我可以想出一些技巧:

首先,我们分配一个包含两个参数回调和上下文数据的池

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 };
然后,为了绑定第一个参数,我们可以执行如下操作

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];
}
但绑定函数也必须显式释放

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;
}
int解除分配\u绑定\u cback(无效(*f)(int))
{

对于(inti=0;i而言,简短的答案是否定的

您唯一能做的就是声明另一个内置了
secret\u id
的函数。如果您使用的是C99或更高版本,您可以将其设置为一个内联函数,以至少限制函数调用开销,尽管更新的编译器本身也可以这样做


坦率地说,这就是std::bind所做的一切,因为它返回一个模板结构,std::bind只是声明了一个内置了secret_id的新functor。

正如6502所解释的,在portable C中,如果不向回调传递某种上下文参数,即使它没有命名
secret_id 直接。但是,有些库(如)可以通过不可移植的方式创建带有附加信息(闭包)的C函数。这些库通过调用汇编或编译器扩展发挥了它们的魔力,但它们被移植到许多流行的平台上;如果它们支持您关心的体系结构,它们工作得很好

从中可以看出,蹦床启用的代码示例是一个高阶函数,它采用参数
a
b
c
(类似于您的
secret\u id
,并返回一个正好包含一个参数
x
的函数,该函数计算
a*x^2+b*x+c

#include <trampoline.h>

static struct quadratic_saved_args {
    double a;
    double b;
    double c;
} *quadratic_saved_args;

static double quadratic_helper(double x) {
    double a, b, c;
    a = quadratic_saved_args->a;
    b = quadratic_saved_args->b;
    c = quadratic_saved_args->c;
    return a*x*x + b*x + c;
}

double (*quadratic(double a, double b, double c))(double) {
    struct quadratic_saved_args *args;
    args = malloc(sizeof(*args));
    args->a = a;
    args->b = b;
    args->c = c;
    return alloc_trampoline(quadratic_helper, &quadratic_saved_args, args);
}

int main() {
    double (*f)(double);
    f = quadratic(1, -79, 1601);
    printf("%g\n", f(42));
    free(trampoline_data(f));
    free_trampoline(f);
    return 0;
}
#包括
静态结构二次参数保存参数{
双a;
双b;
双c;
}*二次保存参数;
静态双二次辅助对象(双x){
双a、b、c;
a=二次参数->a;
b=二次参数->b;
c=二次参数->c;
返回a*x*x+b*x+c;
}
双(*二次型(双a、双b、双c))(双){
结构二次型保存的参数*参数;
args=malloc(sizeof(*args));
args->a=a;
args->b=b;
args->c=c;
返回alloc_蹦床(二次型辅助者和二次型保存的参数、参数);
}
int main(){
双人(*f)(双人);
f=二次型(1,-791601);
printf(“%g\n”,f(42));
免费(蹦床_数据(f));
自由蹦床(f);
返回0;
}

不透明类型并在源代码中保密应该做到:

#include <stdio.h>

// Secret.h

typedef struct TagSecret Secret;
typedef void (*SecretFunction)(Secret*, const char* visible);
void secret_call(Secret*, const char* visible);

// Public.c

void public_action(Secret* secret, const char* visible) {
    printf("%s\n", visible);
    secret_call(secret, visible);
}


// Secret.c

struct TagSecret {
    int id;
};

void secret_call(Secret* secret, const char* visible) {
    printf("%i\n", secret->id);
}

void start() {
    Secret secret = { 43534 };
    public_action(&secret, "Hello World");
}


int main() {
    start();
    return 0;
}
#包括
//秘密
typedef结构TagSecret;
typedef void(*SecretFunction)(Secret*,const char*可见);
void secret_调用(secret*,const char*可见);
//公共服务
无效公共操作(机密*机密,常量字符*可见){
printf(“%s\n”,可见);
秘密呼叫(秘密,可见);
}
//秘密
结构标记秘密{
int-id;
};
无效秘密调用(秘密*秘密,常量字符*可见){
printf(“%i\n”,secret->id);
}
void start(){
秘密={43534};
公开行动(和秘密,“你好世界”);
}
int main(){
start();
返回0;
}

(上面没有提到注册回调函数)

闭包确实是在“C”中实现这一点的方法。LibFFI提供闭包,并且可以移植到大多数主要的操作系统:@BojanNikolic Good point,LibFFI比trampoline更受欢迎。使用na示例的答案解决了这个问题