我们说我有一个C ++头文件foo.hpp
:
namespace foo {
int bar(int);
}
我无法使用extern "C"
,因为只需要从命名空间foo
访问它。
是否有可移植(或相对便携)的方式在C文件foo::bar
中声明foo.c
,以便它与使用C ++中foo::bar
的任何人链接?
我知道在具有特定编译器的特定系统上,我可以了解foo::bar
是如何被修改的,并在foo.c
中执行类似的操作:
int ZN3foo3barEi(int x) { /* ... */ }
但这既不便携也不可读。
答案 0 :(得分:3)
extern "C"
可以嵌套(事实上,<cstdio>
之类的标题通常可以正常工作!),因此您只需执行以下操作:
/* C++ code */
namespace foo {
extern "C" {
int bar(int);
}
}
之后,你就像往常一样在C中实现它:
/* C code */
int bar(int x) {
return -x; /* or whatever */
}
不幸的是,如果存在命名冲突(例如,如果您同时拥有foo::bar
和 baz::bar
),那么您将无法拥有他们两个,除非他们具有相同的功能。
答案 1 :(得分:1)
包装器是否可以接受?
namespace foo {
int bar(int);
}
extern "C" int callable_from_c(int f) {
return foo::bar(f);
}
答案 2 :(得分:0)
强制您的备份计划(使用包装函数),原因如下:C ++支持重载函数,因此您可以使用bar
的不同实现(使用不同的指纹,甚至是兼容的),而在C中只能有一个实现。如果从C调用它们,bar
中哪些C ++可能性匹配?如果您在C ++中有bar(int)
和bar(long)
,并且想要从C调用bar(3)
,该怎么办?
解决方案是使用一个包装extern "C"
函数来调用适当的非C函数。可以从C调用此函数,您将获得所需的结果。
例如,如果您查看链接器管理的标识符名称,您将看到编译器如何在链接时重写标识符以应对重载。如果在C ++中声明函数extern "C"
,则不会发生这种情况。
另一个原因是您在C中没有名称空间.C函数版本必须在全局名称空间级别可见。您无法在命名空间中隐藏C调用约定,因为它可以从所有C代码中调用,或者您将违反C调用约定。