我正在为嵌入式系统(dsPIC33平台)编写C代码,我正在考虑构建一个可重用的代码库,以便在多个项目中使用。
将库绑定到每个项目的最佳实践是什么?
显然,库将具有一些特定于硬件(因此特定于项目)的依赖关系,因此可以合理地假设它将与每个项目一起编译(而不是以二进制形式链接)。
到目前为止我所提出的是保持库的中心位置,但需要一个特定于项目的libraryConfig.h,其中包括函数定义,宏等。这要求库在其自己的代码中包含头,这意味着项目源目录需要在include路径中(而不仅仅是库源目录)。那种混淆了它们之间的区别 #include ""
和 #include <>
不是吗?
这是正常的吗?
一个非常好的问题和答案并不简单。需要考虑的几件事情。以下是我迄今为止的一些观点。
公共代码与项目本地副本
一个重要的决定是,是否使用从中央位置(贵公司的“重用库”)自动更新的“通用”库代码,或者是否保留项目本地副本。
这在下面详细讨论 这个问题。
中央图书馆的好处是,一次完成的工作可以使许多项目受益。项目本地副本的难点在于任何错误修复和改进都不会返回到中央库,并且中央库中的任何错误修复都可能无法进入您的项目。
但是使用中央图书馆的一个潜在困难是,如果他们的特定人员以不受控制的方式修改它以适应他们的项目,并且无意中打破了其他项目。我亲眼看到,在“常见”代码中充满了#ifdefs并且经常打破其他项目。
为了从公共代码(即中央重用库)中获得良好的价值:
图书馆:
- 必须有明确定义的要求,API和单元测试
- 必须避免项目特定的代码;它应该是通用的
- 应该有一个机制来干净地设置项目特定的设置(这可以看作是API的一部分,有效)
- 必须有一个正式的发布过程,版本号和修复,必须跟踪问题。
个别项目:
- 不应该自动盲目地获得“最新”,但应该能够获得具有指定版本号的特定“发布”。然后项目应该控制是否/何时更新到更新的版本。该项目应该能够清楚地跟踪,“我们正在使用库xyz的1.2.3版”。
- 如果可能的话,应该避免“分叉”库代码。例如。避免向库代码添加特定于项目的“功能”。
- 应跟踪对库代码的任何本地修改
- 应该把bug视为库错误,如果可能的话,在中央库中修复。该公司应该有进程来修复它们在中央库中,使用自己的单元测试套件测试库(可能会改进单元测试以便将来捕获bug)。然后根据需要发布新版本的中央库,并在这些项目看起来合适时部署到其他项目。
如果公司没有这样的流程,那么项目应该只制作一段代码的本地副本(比如从以前的项目中复制),然后从那时起承担全部项目本地责任。在这种情况下,您仍然可以从重用中获得一些好处,因为您不是从头开始重写它。
项目特定配置
如果代码需要特定于项目的配置,理想情况下应该尽可能保持代码的一小部分 - 而不是分散在一堆源文件中。理想情况下,单个头文件。但很可能是一个.C文件(比方说,如果你需要定义一些查找表)。该库应该提供一个模板,选项评论很好。
有关如何完成此操作的一个很好的示例,请参阅 μC/ OS-II RTOS (书来自Jean Labrosse Micrium公司。
它并没有搞砸这种区别,无论如何几乎完全是平台定义的。唯一定义的行为是如果包含使用 ""
无法找到该文件,然后再次搜索,就像你说的那样 <>
。
我认为你做的是正确的。根据我的经验,处理特定于平台的标题的正常方法是,给它一个名字,尽可能自信地永远不会与其他任何东西发生碰撞,并将#include与 ""
。然后你告诉平台搬运工做任何必要的编译器特定事情,以确保找到它。通常,这意味着指定一些编译器参数,如-I,用于他想要保留文件的位置。是的,他的项目目录之一。但如果一切都失败了,他总是可以将他的文件复制到他的编译器所在的某个地方。他甚至可以将它复制到他的库源的本地副本中,如果他的编译器对整个事情来说是不合理的困难的话。
另一种方法是在库中有一个文件selectplatform.h,如下所示:
// obviously WIN32 isn't an embedded platform, and GCC is too broad
// to be supported by a single header file. Replace with whatever platforms
// it is you do support out of the box.
#if _WIN32
#include "platforms/msvc32.h"
#elif __GNUC__
#include "platforms/gcc.h"
#else
#error "You must add a new clause to selectplatform.h for your platform"
#endif
这避免了编译器配置的需要,但是每个新平台端口都必须修改文件。如果你是唯一一个做任何移植的人,那肯定不是问题。否则,一个文件由第三方分叉。然后他们可能会添加一个新文件 platforms/
在你的图书馆,或者他们可能把他们的文件放在别处。所以对于第三方来说,它是唯一的 大概 不是问题。如果他们和你们都想要,他们可以将他们的更改(可能包括他们的平台标题)提供回上游。
没有。
通常,您使用编译器中的命令标志定义lib的包含目录的路径(通常为-I标志)。
比方说,如果你正在使用GCC编译器,你的库的头文件都在
/usr/local/include/mylibheaders
那么你必须使用以下选项调用编译器:
-I/usr/local/include/mylibheader/mycurrentplatform
哪里 mycurrentplatform 每个项目的目录都不同,并包含特定于项目的目录 libraryConfig.h
因此,你可以使用 #include<libraryConfig.h>
在每个项目中。
这实际上是一个配置管理问题,而不是C问题。根据我的经验,使用一个好的版本控制程序是最有帮助的。找到一个允许您通过从几个不同位置提取源代码来定义“项目”的方法。意识到您的版本控制程序对“项目”的定义将成为构建项目的基本要素。
能够对项目分支的库代码进行更改并将其检入到版本控制系统中几次也很重要,而无需检查主库位置的更改,直到更改得到证实,因为它们可能影响许多不同的项目。
您的库模块也可能最终得到一个文件,该文件为每个特定项目定义库选项。我采用的一种做法是命名这些接口文件_PAL.h,其中_PAL表示项目抽象层文件。