为什么 __cplusplus 在 extern "C" 中定义

Why is __cplusplus defined within extern "C"

在 extern "C" { } 中,宏 __cplusplus 仍然被定义。当我想在动态加载的库的 header 中包含 mpi.h 的 C 版本时,这将不起作用,因为 mpi.h 仍然会找到 __cplusplus 并且仍然会加载它是由 C++ 打开的。

#undef __cplusplus 适用于 gcc。但是我不想依赖这个。

那么如何编写一个 C++ 程序 - 使用 C++ 版本的 mpi 和 - 与使用 mpi 的 C-Version 的 C-library 链接(其中 #include <mpi.h> 已经出现在 header?

示例代码:

library.h:

#ifdef __cplusplus
extern "C" {
#endif

#include <mpi.h>

void library_do(MPI_Comm comm);

#ifdef __cplusplus
}
#endif

program.cpp:

#include <library.h>

#include <mpi.h>

int main() {
  MPI::Init();
  // do some mpi C++ calls...  
  library_do(MPI::COMM_WORLD);
  MPI::Finalize();
}

万一有人想在这里玩这个例子 library.c:

#include <stdio.h>
#include "library.h"

void library_do(MPI_Comm comm)
{
    int rank;
    MPI_Comm_rank(comm, &rank);
    printf("MPI Rank: %d", rank);
}

并编译我尝试使用的所有内容

mpicc -shared library.c -o lib.so
mpicxx program.cpp -l lib.so
如果编译器是 C++ 编译器,

__cplusplus 将始终由编译器定义。 extern "C" {} 只给你 C 链接,所以里面的代码在 C 编译器上运行得很好。

因为它们是不同的东西。 extern "C" {} 告诉编译器如何导出符号 (see here),而 __cplusplus 告诉您可以使用 C++ 代码,因此您的库可以在 #ifdef 之间使用不同的代码路径。

当然是定义了。它仍然是一个 C++ 编译器,它编译了 extern "C" 块中的代码。它不会停止将代码视为 C++,只是确保使用 C calling/naming 约定。

如果头文件不能被 C++ 编译器编译,唯一的办法是创建一个 C 包装器,公开一个 C++ 兼容的 API.

使用的要点

#ifdef __cplusplus
extern "C" {
#endif

...

#ifdef __cplusplus
}
#endif

是为了防止 C++ 进行的名称重整。我们基本上是说不要像传统的 C++ 函数调用那样使用名称修饰,而是让它不加修饰。 这个 link 可能会有用 Name mangling

它用于使 C 头文件与 C++ 兼容。 标志 __cplusplus 在 C++ 编译器中自动定义。

如果您 #include <mpi.h> 来自 C++ 程序,请不要在其周围放置 extern "C"。至少 OpenMPI 和 Intel MPI 会在 header 本身为你做这件事——如果定义了 __cplusplus

您可能会遇到麻烦,因为某些 MPI 实现仍在 mpi.h 中定义已从标准中删除的 C++ 接口。这显然违反了 extern "C"。例如,对于 OpenMP,您可以通过设置 OMPI_SKIP_MPICXX 来跳过它。然后双 extern "C" 工作,但我仍然不推荐它。

更新:如果您无法修改库 header,只需 #include <mpi.h> 之前 #include <lib.h>

就是说,您不应该为 MPI 使用 C++ 绑定。他们在 6 年多前 从标准中删除,在标准使用了 3 年后...

编译器输出如下:

In file included from /usr/include/c++/4.8.2/bits/stl_algobase.h:61:0,
                 from /usr/include/c++/4.8.2/bits/stl_tree.h:61,
                 from /usr/include/c++/4.8.2/map:60,
                 from /usr/include/openmpi-x86_64/openmpi/ompi/mpi/cxx/mpicxx.h:38,
                 from /usr/include/openmpi-x86_64/mpi.h:2674,
                 from x1.cpp:6:
/usr/include/c++/4.8.2/bits/cpp_type_traits.h:72:3: error: template with C linkage
   template<typename _Iterator, typename _Container>
   ^
/usr/include/c++/4.8.2/bits/cpp_type_traits.h:85:3: error: template with C linkage
   template<bool>
   ^
...

mpi.h header 检测到它被编译为 C++,因此包含 C++ 特定功能。然而,模板(除其他事项外)不适用于 C 链接(即如果 header 在 extern "C" 块内)。

移动上面的包含 extern "C":

#include <mpi.h>

#ifdef __cplusplus
extern "C" {
#endif

void library_do(MPI_Comm comm);

#ifdef __cplusplus
}
#endif