我有理由不想依赖特定的构建系统。我并不是想否定任何人的最爱,但我真的只是想坚持使用编译器附带的内容。在这种情况下,海湾合作委员会。 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++ 的人来说,所有这些都在正常的一天工作之内:
MiniBasic 代码利用了 C 愿意容忍从
void*
任何其他指针类型自动转换的特性。 C++ 让你变得明确。较新的编译器不再容忍
"Hello, world!\n"
在上下文中使用 C 字符串常量(例如 )char*
。标准规定编译器可以将它们放入只读内存中,因此您需要使用const char*
.
就是这样。只需几分钟即可修复 GCC 投诉。
我必须basic.c
对链接的补丁文件中的内容进行一些类似的更改script.c
。我没有费心发布差异,因为它们只是更多相同。
要了解解决此问题的另一种方法,请研究SQLite合并, 相比于SQLite 源代码树。 SQLite 不会将#include
所有其他文件使用到一个主文件中;他们实际上是串联的在一起,但这也是#include
C 或 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
那么dlsym
就God
;在 Linux 上你可以有binfmt_misc使其更加透明的技巧。
如果您坚持拥有独立的程序,请注意 GCC 有-f独立式选项。然后由您提供一个运行时环境(至少对于一些基本的 I/O 来说)系统调用您需要连接哪些接口,请参阅系统调用(2))。认真研究相关内容ABI规格对于 Linux/x86-64 来说是这里。阅读调用约定。
您还可以使用编译器插件自定义 GCC,例如熔化(然后你可以添加一个#pragma
可以做任意事情的东西,甚至可能分叉一个gcc
......)
顺便说一句,一个入口点就像你的God
不是源文件,而是链接器已知的一些标签。
阅读更多有关编译器,连接子(看莱文的书:链接器和加载器,目标文件,极低频,图书馆和共享对象(参见德雷珀的纸: 如何编写共享库...),crt0.o应该有帮助。
另请参阅C++ 模块提案草案 N3347