我有一个 10+ 岁的 C 库,我相信它在过去的日子里工作得很好,但是当我尝试将其与C++源(包含主函数)一起使用时,前几天我遇到了一些困难。
编辑:澄清一下,C库可以用gcc
编译得很好,它会old_c_library.o
生成一个目标文件。此库的使用方式应该使 C 头文件old_c_library.h
在 main.c
C 源文件中#include
d。然后,您的主 C 源文件应通过 gcc
编译并与old_c_library.o
链接在一起。在这里,我想改用C++源文件main.cpp
,并使用g++
编译/链接它。
在编译C++源文件的过程中,出现了以下三个问题:
- C 库的一个头文件包含C++保留字
new
(它是整数的名称),这导致了致命错误;和 - C 库的一个头文件包含
calloc
调用(缺少显式类型转换),这导致了致命错误;和 - C 库的各种文件包含有符号整数和无符号整数比较的代码,这导致了警告。
编辑:正如评论中所建议的那样,我尝试使用#extern "C" { #include "obsolete_c_library.h" }
"技巧",但这并没有解决我的任何问题。
我可以通过重命名保留字的所有实例并将它们替换为 - 基本上 - 其他任何东西来解决问题 1。我可以通过类型转换calloc
调用来解决问题 2。我可能会尝试通过这里建议的想法来整理警告:如何禁用几行代码的 GCC 警告。
但我仍然想知道,有没有办法以一种优雅、高级的方式克服这些困难,而不实际接触原始库?
相关:C 不是C++的子集在哪里?我是否投射了马洛克的结果?以及如何使用 extern 在源文件之间共享变量?。
预期这种用法的情况下构建的,那么将 C 头文件#include
到C++源中是不安全的。 在某些情况下,它可以工作,但您需要准备好修改标头或为要访问的函数和全局变量编写自己的声明。
至少,如果 C 标头声明了任何函数,并且您没有在 C++ 中重新编译这些函数,则必须确保在C++代码中为声明分配了 C 链接。 C 标头通过条件编译指令自动解释这一点并不少见,但如果它们没有,那么您可以通过将包含包含在 C 链接块中来在另一端执行此操作:
extern "C" {
#include "myclib.h"
}
如果 C 标头声明的全局变量的名称与C++关键字冲突,并且不需要引用这些全局变量,则可以使用预处理器重新定义它们:
#define new extern_new
#include "myclib.h"
#undef new
这不能保证有效,但值得一试。 不要忘记在包含 C 标头后#undef
此类宏,如下所示。
您可能还有其他有趣的技巧可以使用宏来使特定的标头适应C++,但在某些时候,仅在主C++源或您自己的C++标头中复制/重写所需的声明(并且仅复制/重写这些声明)更有意义。 请注意,这样做并不能消除声明 C 链接的需要 - 该要求来自由 C 编译器而不是C++编译器编译的库。
-
您可以编写 c 标头的副本,唯一的区别是缺少
extern int new
声明。然后使用新创建的 c++ 友好标头,而不是旧的、不兼容的标头。如果需要从 c++ 引用该变量,则需要执行更复杂的操作。你需要用 c 编写一个新的包装库,它向
读取和写入函数公开一个指向 c++ 的指针,可用于访问不幸命名的变量。如果某些内联函数引用变量,则可以从重复标头中省略它们,如果需要从 c++ 调用它们,请以 c++ 友好的方式重新实现它们。只是不要在
extern "C"
中给重新实现相同的名称,因为这会让你与一些现有的c代码可能使用的原始内联函数发生冲突。 -
执行与 1 中所述相同的标头复制。 省略有问题的内联函数,并在需要时重新实现。
-
如您所知,可以忽略或禁用警告。无需修改原始标头。你可以用不生成警告的版本重写任何{const,type}不安全的内联函数,但你必须考虑你的时间是否值得。
这种方法的缺点是,您现在有两个部分/全部标头。C++ 使用的新代码,以及某些旧代码可能使用的旧代码。
如果你可以放弃不接触原始库的要求,并且不需要使用现有的编译二进制文件,那么一个优雅的解决方案是简单地重命名有问题的变量(1.)。使不兼容 C++ 的内联函数 (2.) 非内联并将它们移动到 C 源文件中。修复生成警告的不安全代码 (3.),或者如果警告特定于 c++,只需将函数设置为非内联即可。