什么样的 C++ 函数可以放在 C 函数指针中?
What kinds of C++ functions can be placed in a C function pointer?
我有一个 C 库,它使用函数指针结构进行回调。将从 C 代码调用回调。
extern "C" {
typedef struct callbacks_t {
void (*foo) (const char*);
int (*bar) (int);
} callbacks_t;
}// extern C
我可以安全地将哪些类型的 C++ 函数放在那些函数指针中,以便从 C 库中调用?静态成员函数?完全指定的模板函数?非捕获 Lambda?
g++ 似乎可以让我使用以上所有功能,但我质疑将不同的调用约定和语言绑定用于 C 和 C++ 函数时的安全性。
一般来说,除非你使用cast,否则你应该相信g++。
虽然您提到的 none 函数类型确实可以导出以在 C 中使用,但这不是您要问的。你问的是什么函数可以作为函数指针传递。
回答你能通过的,我觉得了解你不能通过的更有建设性。您不能传递任何需要参数列表中未明确说明的额外参数的内容。
所以,没有非静态方法。他们需要一个隐含的 "this"。 C不知道要通过它。再一次,编译器不会让你。
没有捕获 lambda。他们需要一个隐式参数与实际的 lambda 主体。
您可以传递的是不需要隐式上下文的函数指针。事实上,您继续列出了它们:
- 函数指针。不管是标准函数还是模板,只要模板完全解析就可以了。这不是问题。您编写的任何导致函数指针的语法都将自动完全解析模板。
- 非捕获 lambda。这是 C++11 在引入 lambda 时引入的一种特殊变通方法。由于可以这样做,编译器会进行所需的显式转换。
- 静态方法。因为它们是静态的,所以它们不会被隐式传递
this
,所以它们没问题。
最后一个值得继续扩展。许多 C 回调机制都有一个函数指针和一个 void* opaq。以下是将它们与 C++ class:
一起使用的标准且相当安全
class Something {
void callback() {
// Body goes here
}
static void exported_callback(void *opaq) {
static_cast<Something*>(opaq)->callback();
}
}
然后做:
Something something;
register_callback(Something::exported_callback, &something);
编辑添加:
这样做的唯一原因是因为当没有传递隐式参数时,C++ 调用约定和 C 调用约定是相同的。名称修饰有所不同,但当您传递函数指针时它并不相关,因为名称修饰的唯一目的是让链接器找到正确的函数地址。
如果您尝试使用预期回调的技巧,例如 stdcall 或 pascal 调用约定,这个方案就会完全失败。
然而,这并不是静态方法、lambda 和模板函数所独有的。在这种情况下,即使是标准函数也会失败。
遗憾的是,当您定义一个指向 stdcall 类型的函数指针时,gcc 会忽略您:
#define stdcall __attribute__((stdcall))
typedef stdcall void (*callback_type)(void *);
结果:
test.cpp:2:45: warning: ‘stdcall’ attribute ignored [-Wattributes]
typedef stdcall void (*callback_type)(void *);
I have a C library that uses a struct of function pointers for callbacks. The callbacks will be called from C code.
C 库只理解 C。所以你只能传回 C 明确支持和理解的东西。
由于在 C++ 中没有定义任何函数类型的调用约定,因此您不能显式传回 C++ 函数。您只能传回 C 函数(那些用 extern "C"
显式声明的函数)并保证它们兼容。
像所有未定义的行为一样,这似乎可行(例如传回正常的 C++ 函数或静态成员)。但就像所有未定义的行为一样,它是允许工作的。你只是不能保证它实际上是正确的或可移植的。
extern "C" {
typedef struct callbacks_t {
void (*foo) (const char*);
int (*bar) (int);
} callbacks_t;
// Any functions you define in here.
// You can set as value to foo and bar.
}// extern C
What kinds of C++ functions can I safely place in those function pointers to be called from the C library?
Static member functions?
没有。但这恰好适用于很多平台。这是一个常见的错误,会在某些平台上咬人。
Fully specified template functions?
没有。
Non-capturing Lambdas?
没有
g++ seemingly lets me use all of the above,
是的。但假设您要传递给使用 C++ 编译器构建的对象(这将是合法的)。因为 C++ 代码将使用正确的调用约定。问题是当您将这些东西传递给 C 库时(想到 pthreads)。
我有一个 C 库,它使用函数指针结构进行回调。将从 C 代码调用回调。
extern "C" {
typedef struct callbacks_t {
void (*foo) (const char*);
int (*bar) (int);
} callbacks_t;
}// extern C
我可以安全地将哪些类型的 C++ 函数放在那些函数指针中,以便从 C 库中调用?静态成员函数?完全指定的模板函数?非捕获 Lambda?
g++ 似乎可以让我使用以上所有功能,但我质疑将不同的调用约定和语言绑定用于 C 和 C++ 函数时的安全性。
一般来说,除非你使用cast,否则你应该相信g++。
虽然您提到的 none 函数类型确实可以导出以在 C 中使用,但这不是您要问的。你问的是什么函数可以作为函数指针传递。
回答你能通过的,我觉得了解你不能通过的更有建设性。您不能传递任何需要参数列表中未明确说明的额外参数的内容。
所以,没有非静态方法。他们需要一个隐含的 "this"。 C不知道要通过它。再一次,编译器不会让你。
没有捕获 lambda。他们需要一个隐式参数与实际的 lambda 主体。
您可以传递的是不需要隐式上下文的函数指针。事实上,您继续列出了它们:
- 函数指针。不管是标准函数还是模板,只要模板完全解析就可以了。这不是问题。您编写的任何导致函数指针的语法都将自动完全解析模板。
- 非捕获 lambda。这是 C++11 在引入 lambda 时引入的一种特殊变通方法。由于可以这样做,编译器会进行所需的显式转换。
- 静态方法。因为它们是静态的,所以它们不会被隐式传递
this
,所以它们没问题。
最后一个值得继续扩展。许多 C 回调机制都有一个函数指针和一个 void* opaq。以下是将它们与 C++ class:
一起使用的标准且相当安全class Something {
void callback() {
// Body goes here
}
static void exported_callback(void *opaq) {
static_cast<Something*>(opaq)->callback();
}
}
然后做:
Something something;
register_callback(Something::exported_callback, &something);
编辑添加: 这样做的唯一原因是因为当没有传递隐式参数时,C++ 调用约定和 C 调用约定是相同的。名称修饰有所不同,但当您传递函数指针时它并不相关,因为名称修饰的唯一目的是让链接器找到正确的函数地址。
如果您尝试使用预期回调的技巧,例如 stdcall 或 pascal 调用约定,这个方案就会完全失败。
然而,这并不是静态方法、lambda 和模板函数所独有的。在这种情况下,即使是标准函数也会失败。
遗憾的是,当您定义一个指向 stdcall 类型的函数指针时,gcc 会忽略您:
#define stdcall __attribute__((stdcall))
typedef stdcall void (*callback_type)(void *);
结果:
test.cpp:2:45: warning: ‘stdcall’ attribute ignored [-Wattributes]
typedef stdcall void (*callback_type)(void *);
I have a C library that uses a struct of function pointers for callbacks. The callbacks will be called from C code.
C 库只理解 C。所以你只能传回 C 明确支持和理解的东西。
由于在 C++ 中没有定义任何函数类型的调用约定,因此您不能显式传回 C++ 函数。您只能传回 C 函数(那些用 extern "C"
显式声明的函数)并保证它们兼容。
像所有未定义的行为一样,这似乎可行(例如传回正常的 C++ 函数或静态成员)。但就像所有未定义的行为一样,它是允许工作的。你只是不能保证它实际上是正确的或可移植的。
extern "C" {
typedef struct callbacks_t {
void (*foo) (const char*);
int (*bar) (int);
} callbacks_t;
// Any functions you define in here.
// You can set as value to foo and bar.
}// extern C
What kinds of C++ functions can I safely place in those function pointers to be called from the C library?
Static member functions?
没有。但这恰好适用于很多平台。这是一个常见的错误,会在某些平台上咬人。
Fully specified template functions?
没有。
Non-capturing Lambdas?
没有
g++ seemingly lets me use all of the above,
是的。但假设您要传递给使用 C++ 编译器构建的对象(这将是合法的)。因为 C++ 代码将使用正确的调用约定。问题是当您将这些东西传递给 C 库时(想到 pthreads)。