可重用嵌入式C的最佳实践?

时间:2009-11-26 19:37:35

标签: c embedded

我正在为嵌入式系统(dsPIC33平台)编写C代码,我正在考虑建立一个可重用的代码库,以便在多个项目中使用。

将图书馆绑定到每个项目的最佳做法是什么?

显然,库将具有一些特定于硬件(因此特定于项目)的依赖关系,因此可以合理地假设它将与每个项目一起编译(而不是以二进制形式链接)。

到目前为止,我提出的是保持库的中心位置,但需要一个包含函数定义,宏等的项目特定的libraryConfig.h。这要求库在其自己的代码中包含头,这意味着项目源目录需要在include路径中(而不仅仅是库源目录)。这种混淆了#include ""#include <>之间的区别,不是吗?

这是正常的吗?

4 个答案:

答案 0 :(得分:8)

一个非常好的问题,答案并不简单。需要考虑的几件事情。以下是我迄今为止的一些观点。

公共代码与项目本地副本

一个重要的决定是,是否使用从中央位置(贵公司的“重用库”)自动更新的“通用”库代码,或者是否保留项目本地副本。

这在this SO question中有详细讨论。

中央图书馆的好处是,一次完成的工作可以使许多项目受益。项目本地副本的难点在于,任何错误修复和改进都不会返回到中央库,并且中央库中的任何错误修复都可能无法进入您的项目。

但是使用中央图书馆的一个潜在困难是,如果他们的特定人员以不受控制的方式修改它以适应他们的项目,并且无意中打破了其他项目。我亲眼看到,在“常见”代码中充满了#ifdef并且经常打破其他项目。

为了从公共代码(即中央重用库)中获得良好的价值:

图书馆:

  • 必须具有明确定义的要求,API和单元测试
  • 必须避免项目特定的代码;它应该是通用的
  • 应该有一个机制来干净地设置项目特定的设置(这可以看作是API的一部分,有效)
  • 必须有正式的发布流程,包含版本号和修订,必须跟踪问题。

个别项目:

  • 不应自动盲目地获取“最新”,但应该能够获得具有指定版本号的特定“发布”。然后项目应该控制是否/何时更新到更新的版本。该项目应该能够清楚地跟踪,“我们正在使用库xyz的1.2.3版本。”
  • 如果可能的话,
  • 应该避免“分叉”库代码。例如。避免将特定于项目的“功能”添加到库代码中。
  • 应跟踪对库代码的任何本地修改
  • 应该将bug视为库错误,如果可能的话,将在中央库中修复。公司应该拥有在中央库中修复它们的流程,使用自己的单元测试套件测试库(可能会改进单元测试以便将来捕获bug)。然后根据需要发布新版本的中央库,并在/当这些项目看起来合适时部署到其他项目。

如果公司没有这样的流程,那么项目应该只制作一段代码的本地副本(比如从以前的项目中复制),然后从那时起承担全部的项目本地责任。 。在这种情况下,你仍然可以从重用中获得一些好处,因为你不是从头开始重写它。

项目特定配置

如果代码需要特定于项目的配置,理想情况下应该尽可能保持代码的一小部分 - 而不是分散在一堆源文件中。理想情况下,单个头文件。但很可能是一个.C文件(比方说,如果你需要定义一些查找表)。该库应该提供一个模板,其中有很好的评论选项。

有关如何执行此操作的良好示例,请参阅µC/OS-II RTOS中Jean Labrosse的bookMicrium)。

答案 1 :(得分:1)

它并没有搞砸这种区别,无论如何几乎完全是平台定义的。唯一定义的行为是,如果使用""的包含无法找到该文件,则会再次搜索,就像您说<>一样。

我认为你做的是正确的。根据我的经验,处理特定于平台的标题的正常方法是,给它一个名称,尽可能自信不会与其他任何内容发生冲突,并将#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/添加一个新文件,或者他们可能会将其文件放在其他位置。所以对于第三方来说,只有可能不是问题。如果他们和你们都想要,他们可以将他们的变化(可能包括他们的平台标题)提供回上游。

答案 2 :(得分:1)

没有。
通常,您使用编译器中的命令标志定义lib的包含目录的路径(通常是-I标志)。

比如说,如果您使用的是GCC编译器,那么您的库的头文件位于

/usr/local/include/mylibheaders

然后你必须使用以下选项调用编译器:

-I/usr/local/include/mylibheader/mycurrentplatform

其中 mycurrentplatform 目录对于每个项目都是不同的,并且包含特定于项目的 libraryConfig.h

因此,您可以在每个项目中使用#include<libraryConfig.h>

答案 3 :(得分:1)

这实际上是一个配置管理问题,而不是C问题。根据我的经验,使用一个好的版本控制程序是最有帮助的。找到一个允许您通过从几个不同位置提取源代码来定义“项目”的方法。意识到您的版本控制程序对“项目”的定义将成为构建项目的基本要素。

能够对项目分支的库代码进行更改并将其多次检入版本控制系统,而不必检查主库位置的更改,直到更改得到证实,这一点也很重要。可能会影响许多不同的项目。

您的库模块也可能最终得到一个文件,该文件为每个特定项目定义库选项。我采用的一种做法是命名这些接口文件_PAL.h,其中_PAL表示项目抽象层文件。