将源和标头包含到顶级C包装器中
Including sources and headers into a toplevel C wrapper
我偶然发现了以下代码:
//
// Top-level file that includes all of the C/C++ files required
//
// The C code may be compiled by compiling this top file only,
// or by compiling individual files then linking them together.
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include "my_header.h"
#include "my_source1.cc"
#include "my_source2.cc"
#ifdef __cplusplus
}
#endif
这肯定是不寻常的,但它被认为是不良做法吗?如果是,为什么?
我能想到的一个潜在的负面影响是,一个典型的构建系统很难分析依赖关系。这种技术没有被广泛使用还有其他原因吗?
首先:extern "C" { #include "my_cpp_file.cc" }
就是加不起来。。。无论如何,我会尝试用一个实际的例子来回答你的问题
请注意,有时确实在源文件中看到#include "some_file.c"
。通常这样做是因为另一个文件中的代码正在开发中,或者不确定该文件中正在开发的功能是否会发布<另一个原因很简单:为了提高可读性:不必滚动太多),甚至:反映你正在线程化。对一些人来说,将孩子的代码放在一个单独的文件中会有所帮助,尤其是在学习线程时。>
当然,将翻译单元包含在一个主翻译单元中(对我来说,这是滥用预处理器,但这不是重点)的主要好处很简单:编译时I/O更少,因此编译更快。这里已经解释过了。
不过,这只是故事的一个方面。这项技术并非完美。这里有几个注意事项。为了平衡"团结的魔力">文章,下面是《团结的邪恶》的文章
总之,这里有一个我反对的简短列表,还有一些例子:
static
全局变量(老实说,我们都使用过它们)extern
和static
函数相似:两者都可以在任何地方调用- 调试需要构建所有,除非(正如"pro">文章所建议的那样)为同一项目准备好Unity build和模块化构建。IMO有点麻烦
- 如果你想从你的项目中提取一个库,以后再使用,那就不合适了(想想通用共享库或DLL)
只需比较这两种情况:
//foo.h
struct foo
{
char *value;
int checksum;
struct foo *next;
};
extern struct foo * get_foo(const char *val);
extern void free_foo( struct foo **foo);
//foo.c
#include <foo.h>
static int get_checksum( const char *val);
struct foo * get_foo( const char *val)
{
//call get_checksum
struct foo *retVal = malloc(sizeof *retVal);
retVal->value = calloc(strlen(val) + 1, 1);
retVal->cecksum = get_checksum(val);
retVal->next = NULL;
return retVal;
}
void free_foo ( struct foo **foo)
{
free(*foo->value);
if (*foo->next != NULL)
free_foo(&(*foo->next));
free(*foo);
*foo = NULL;
}
如果我将这个C文件包含在另一个源文件中,那么get_checksum
函数也可以在该文件中调用。在这里,情况并非如此
名称冲突也会更加常见。
想象一下,如果您编写一些代码来轻松执行某些快速MySQL查询。我会编写自己的头文件和源文件,并像这样编译它们:
gccc -Wall -std=c99 mysql_file.c `mysql_config --cflags --libs` -o mysql.o
只需在其他项目中使用mysql.o
编译的文件,只需如下链接即可:
//another_file.c
include <mysql_file.h>
int main ( void )
{
my_own_mysql_function();
return 0;
}
然后我可以这样编译:
gcc another_file.c mysql.o -o my_bin
这节省了开发时间、编译时间,并使您的项目更易于管理(前提是您知道如何处理make文件)。
这些.o文件的另一个优点是在项目上进行协作。假设我将为我们的mysql.o
文件宣布一个新特性。在我处理代码时,所有将我的代码作为依赖项的项目都可以安全地继续使用最后一个稳定编译的mysql.o
文件
完成后,我们可以使用稳定的依赖项(其他.o文件)测试我的模块,并确保我没有添加任何错误。
问题是每次包含头时,都会编译每个*.cc
文件。
例如,如果您有:
// foo.cc:
// also includes implementations of all the functions
// due to my_source1.cc being included
#include "main_header.h"
和:
// bar.cc:
// implementations included (again!)
// ... you get far more object code at best, and a linker error at worst
#include "main_header.h"
不相关,但仍然相关:有时,当您的头在C++代码中包含C stdlib头时,编译器会遇到问题。
编辑:如上所述,在C++源代码周围还有extern "C"
的问题。
这肯定是不寻常的,但它被认为是糟糕的做法吗?如果是,为什么?
您可能看到的是"Unity Build"。如果配置正确,Unity构建是一种不错的方法。最初将库配置为以这种方式构建可能会有问题,因为由于可见性的扩展,可能会出现冲突——包括作者打算对翻译保密的实现。
但是,如果extern "C"
块,则定义(在*.cc
中)应该在外部。
我能想到的一个潜在的负面因素是,一个典型的构建系统很难分析依赖关系。这种技术没有被广泛使用还有其他原因吗?
它降低了依赖性/复杂性,因为翻译计数会下降。
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从包含m行的文件中提取n行,必要时(惰性地)重复该文件
- 编译包含字符串的代码时遇到问题
- c++库的公共头文件中应该包含什么
- 如何在c++17中制作一个模板包装器/装饰器
- 将包含C样式数组的对象初始化为成员变量(C++)
- 是否需要删除包含对象的"pair"?
- 在 C++ 对象包装器中安全地包含 C 代码
- std::包含 std::函数回调的多个包装器的向量不起作用
- C 函数调用包装器包含类成员功能作为模板参数
- 将仅标头库的包含包装在单个 cpp 文件中
- 将源和标头包含到顶级C包装器中
- 当包含在另一个向量中时,如何使用矢量包装程序类
- 如何让SWIG在包装包含向量的模板类时应用模板
- 如何创建一个仅包含 C 代码包装C++代码的 .so 文件 - 与 OpenCV 相关
- 如何在 Unity 3D 中包含 SWIG 包装的C++
- 我应该包装一个完全包含在node.js函数中的C++对象吗
- 如何使用SWIG包装许多.h文件并包含任何依赖项
- 包含 C 库时,何时应将包含包装在"外部"C""中
- 包装器包含()函数用于不同的容器