C++ 在C中模拟std::bind
我使用std::bind提供回调,同时通过首先绑定一些参数来抽象一些逻辑。i、 eC++ 在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);
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示例的答案解决了这个问题