通过在实际 C/C++ 源文件中包含预处理器构建指令来编译 C/C++ 代码

通过在实际 C/C++ 源文件中包含预处理器构建指令来编译 C/C++ 代码

我有理由不想依赖特定的构建系统。我并不是想否定任何人的最爱,但我真的只是想坚持使用编译器附带的内容。在这种情况下,海湾合作委员会。 Automake 存在某些兼容性问题,尤其是与 Windows 的兼容性问题。 <3 GNU make 的局限性很大,常常需要用 shell 脚本来补充。 Shell 脚本可以采取多种形式,长话短说,可能会激怒很多人,这就是我想做的——

主要的切入点是神。无论是 C 还是 C++ 源文件,它都是应用程序的中心。我不仅希望主入口点成为第一个执行的东西,而且还希望它成为第一个编译的东西。让我解释 -

曾经有一段时间,专有库和闭源库很常见。感谢苹果转向 Unix 和微软搬起石头砸自己的脚,那个时代已经结束了。任何需要动态链接的库都可以作为应用程序的支持文件包含在内。因此,.SO(也许还有 .DLL;])的单独构建指令都很好,因为它们是单独的可执行文件。任何其他库都应该静态链接。现在,我们来谈谈静态链接——

静态链接真是个混蛋。这就是 makefile 的用途。如果整个项目是用一种语言(例如 C 或 C++)编写的,则可以 #include 库作为标头。那就好了。但现在,让我们考虑另一种情况——

假设您像我一样,无法找出 C 对字符串的困难借口,因此您决定使用 C++。但您想使用 C 库,例如 MiniBasic。上帝帮助我们。如果 C 库的设计不符合 C++ 的语法,那么你就完蛋了。这时 makefile 就派上用场了,因为您需要使用 C 编译器编译 C 源文件,并使用 C++ 编译器编译 C++ 源文件。我不想使用 makefile。

我希望有一种方法可以利用 GCC 的预处理器宏来告诉它这样的事情:

嗨,海湾合作委员会。你好吗?如果您忘记了,您现在正在查看的这个源文件是用 C++ 编写的。你当然应该用G++编译它。该文件还需要另一个文件,但它是用 C 编写的。它称为“lolcats.c”。我希望您使用 GCC 将其编译为目标文件,并希望您使用 G++ 将其编译为主目标文件,然后我希望您将它们链接在一起形成可执行文件。

我如何用预处理器术语写这样的东西? GCC 甚至这样做吗?

答案1

主要的切入点是神。无论是 C 还是 C++ 源文件,它都是应用程序的中心。

仅以同样的方式氮是松树的中心。这是一切的地方开始,但是 C 或 C++ 没有任何东西可以让您将应用程序的“中心”放在main().

许多 C 和 C++ 程序都构建在事件循环或一个输入/输出泵。这些是此类计划的“中心”。您甚至不必将这些循环放在同一个中模块作为main()

我不仅希望主入口点成为第一个执行的东西,而且还希望它成为第一个编译的东西。

其实最容易放main() 最后的在 C 或 C++ 源文件中。

C 和 C++ 不像某些语言,可以在声明之前使用符号。首先main()意味着你必须前置声明其他一切。

曾经有一段时间,专有库和闭源库很常见。由于苹果转向 Unix 和微软搬起石头砸自己的脚,那个时代已经结束了。

告诉他他在做梦!

OS X 和 iOS 是满的专有代码,微软不会很快消失。

无论如何,微软目前的困难与你的问题有什么关系?您说您可能想要制作 DLL,并且您提到 Automake 无法有效地应对 Windows。这告诉我,微软在你们的世界中仍然具有重要意义。

静态链接真是个混蛋。

真的吗?我一直都找到了更轻松而不是链接到动态库。这是一种更古老、更简单的技术,出错的情况也更少。

静态链接将外部依赖项合并到可执行文件中,以便可执行文件独立、自包含。从你问题的其余部分来看,这应该对你有吸引力。

您可以#include 库作为标头

不……你#include 库头,而不是图书馆。

这不仅仅是迂腐。术语事情。它是有意义的。

如果你可以#include图书馆,#include </usr/lib/libfoo.a>那就行了。

在许多编程语言中,外部模块/库引用的工作方式。也就是说,您直接引用外部代码。

C 和 C++ 不属于以这种方式工作的语言。

如果 C 库的设计不符合 C++ 的语法,那么你就完蛋了。

不,你只需要学习使用 C++ 即可。具体在这里,extern "C"

我如何用预处理器术语写这样的东西?

#include它对于另一个 C 或 C++ 文件是完全合法的:

#include <some/library/main.cpp>

#include <some/other/library/main.c>
#include <some/other/library/secondary_module.c>

#include <iostream>

int main()
{
   call_the_library();
   do_other_stuff();
   return 0;
}

我们不在extern "C"这里使用,因为这会将 C 和 C++ 代码从其他库直接提取到我们的 C++ 文件中,因此 C 模块也需要是合法的 C++。有许多C 和 C++ 之间令人烦恼的小差异,但是如果你要混合使用这两种语言,你就必须知道如何处理它们。

这样做的另一个棘手部分是,#includes如果是链接器命令,则命令的顺序比库引用的顺序更敏感。当您以这种方式绕过链接器时,您最终必须手动执行一些操作,否则链接器会自动为您执行这些操作。

为了证明这一点,我采取了迷你基本版(您自己的示例)并将其script.c驱动程序转换为独立的 C++ 程序,该程序表示#include <basic.c>而不是#include <basic.h>. (修补)只是为了证明现在它确实是一个C++程序,我改变了所有对流插入printf()的调用cout

我必须做一些其他的改变,对于那些想要混合 C 和 C++ 的人来说,所有这些都在正常的一天工作之内:

  1. MiniBasic 代码利用了 C 愿意容忍从void*任何其他指针类型自动转换的特性。 C++ 让你变得明确。

  2. 较新的编译器不再容忍"Hello, world!\n"在上下文中使用 C 字符串常量(例如 )char*。标准规定编译器可以将它们放入只读内存中,因此您需要使用const char*.

就是这样。只需几分钟即可修复 GCC 投诉。

我必须basic.c对链接的补丁文件中的内容进行一些类似的更改script.c。我没有费心发布差异,因为它们只是更多相同。

要了解解决此问题的另一种方法,请研究SQLite合并, 相比于SQLite 源代码树。 SQLite 不会将#include所有其他文件使用到一个主文件中;他们实际上是串联的在一起,但这也是#includeC 或 C++ 中的全部操作。

答案2

gcc 会将 C、C 和 C++ 编译为 C++,只是因为您正确命名了文件,预处理器不会参与其中。不过,您需要extern "C"在 C++ 文件中的 C 声明周围放置块,以确保链接器可以正确链接内容。

但你所描述的几乎永远不会有帮助。如果您使用的库首先需要某种配置来编译,那么如果您只是尝试将文件直接编译到项目中,它仍然需要它。现实世界中,没有多少重要的库是纯 ISO C 的,无需修改或某种配置脚本即可在每个平台上进行编译,因此大多数时候您会创建复杂性,而不是减少复杂性,因为您将必须以某种方式在您自己的构建中处理该配置。

答案3

不,你不能那样做。没有 gcc pragma这样编译。最相似的是 Microsoft Visual C++#pragma comment(lib, …)#pragma comment(linker, …)[1] [2] 注意一些库应该包含在链接过程中。

嗨,海湾合作委员会。你好吗?如果您忘记了,您现在正在查看的这个源文件是用 C++ 编写的。你当然应该用G++编译它。该文件还需要另一个文件,但它是用 C 编写的。它称为“lolcats.c”。我希望您使用 GCC 将其编译为目标文件,并希望您使用 G++ 将其编译为主目标文件,然后我希望您将它们链接在一起形成可执行文件。

您的请求正是 Makefile 所做的。

您可以创建一个 shell 脚本来提取源文件并使用您想要使用的任何命令来编译它们。但这是错误的解决方案™

如果您有多个文件,请将它们存储为多个文件。如果您希望人们不使用 make 或 cmake,请不要提供Makefile/ CMakefile。例如给他们一个compile-me.sh文件。

我觉得自己处于类似情况的唯一情况是,我在单个文件中编写了一个 util,但我只是在注释的顶部提供了编译命令。

答案4

你可以决定你的所有东西都是插件。然后编译它们gcc -shared -fPIC -O thing1.cc -o thing1.so 并让一个主存根程序执行dlopen(3)./thing1.so那么dlsymGod;在 Linux 上你可以有binfmt_misc使其更加透明的技巧。

如果您坚持拥有独立的程序,请注意 GCC 有-f独立式选项。然后由您提供一个运行时环境(至少对于一些基本的 I/O 来说)系统调用您需要连接哪些接口,请参阅系统调用(2))。认真研究相关内容ABI规格对于 Linux/x86-64 来说是这里。阅读调用约定

您还可以使用编译器插件自定义 GCC,例如熔化(然后你可以添加一个#pragma 可以做任意事情的东西,甚至可能分叉一个gcc......)

顺便说一句,一个入口点就像你的God不是源文件,而是链接器已知的一些标签。

阅读更多有关编译器,连接子(看莱文的书:链接器和加载器,目标文件,极低频,图书馆和共享对象(参见德雷珀的纸: 如何编写共享库...),crt0.o应该有帮助。

另请参阅C++ 模块提案草案 N3347

相关内容