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

How can I implement in C a function declared in a C++ namespace?

本文关键字:声明 函数 命名空间 实现 C++      更新时间:2023-10-16

假设我有一个C++头文件foo.hpp:

namespace foo {
    int bar(int);
}

我不能使用extern "C",因为它只需要从命名空间foo访问。

是否有一种可移植(或相对可移植)的方法可以在C文件foo.c中声明foo::bar,以便它与使用C++中的foo::bar的任何人链接?

我知道,在一个具有特定编译器的特定系统上,我可以发现foo::bar是如何被破坏的,并在foo.c:中执行类似的操作

int ZN3foo3barEi(int x) { /* ... */ }

但这既不可移植,也不可读。

extern "C"可以嵌套(事实上,像<cstdio>这样的头文件通常就是这样工作的!),所以您只需执行以下操作:

/* C++ code */
namespace foo {
    extern "C" {
        int bar(int);
    }
}

之后,您只需像往常一样在C中实现它:

/* C code */
int bar(int x) {
    return -x; /* or whatever */
}

不幸的是,如果存在命名冲突(例如,如果您同时拥有foo::barbaz::bar),您将无法同时拥有它们,除非它们是相同的函数。

包装器可以接受吗?

namespace foo {
    int bar(int);
    }
extern "C" int callable_from_c(int f) {
    return foo::bar(f);
    }

被迫执行备份计划(使用包装函数),原因如下: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调用约定。