如何在C中实现在C ++命名空间中声明的函数?

时间:2015-02-11 18:01:33

标签: c++ c linker namespaces name-mangling

我们说我有一个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) { /* ... */ }

但这既不便携也不可读。

3 个答案:

答案 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调用约定。