跨平台C单头文件和多个实现



我正在为一个廉价的传感器开发一个开源C驱动程序,该驱动程序主要用于Arduino项目。该项目的设置方式可以支持Arduino生态系统之外的多个平台,如Raspberry Pi。

该项目是用platform.h文件设置的,目的是对该头文件进行不同的实现。就像下面的例子:

platform.h
platform_arduino.c
platform_rpi.c
platform_windows.c

有这样一篇文章(跨平台C++代码和单头-多个实现)Stack Overflow深入介绍了如何为C++处理这一问题,但我觉得这些例子都不适用于这个C实现。

我提出了一些解决方案,比如在文件顶部添加每个平台的需求。

#if SOME_REQUIREMENT
#include  "platform.h"
int8_t  t_open(void)
{
// Implementation here
}
#endif //SOME_REQUIREMENT

但这似乎是一个笨拙的解决方案。

  • 它会影响代码的可读性1
  • 这可能会使调试冲突的需求成为一场噩梦

1许多编辑器(如VS代码)试图将不符合要求的代码灰显。虽然我大多数时候都想要这个,但在使用跨平台驱动程序时,这真的很烦人。我可以在整个项目中禁用它,但在项目的其他部分它是有用的。我知道它可能可以用VS代码来解决。然而,我正在寻求为平台选择正确文件/代码的替代方法,因为我有兴趣了解其他策略。

";"问题";对Arduino的支持是主要关注点,这意味着它不能用makefile魔术轻松解决我的问题是,实现这个问题的解决方案的替代方法是什么,这些方法仍然可读

如果没有makefile魔术就无法完成,那么这也是一个答案。


为了参考,这里是头文件和实现的简化示例

平台.h

#ifndef __PLATFORM__
#define  __PLATFORM__
int8_t  t_open(void);
#endif //__PLATFORM__

platform_arduino.c

#include  "platform.h"

int8_t  t_open(void)
{
// Implementation here
}

this(跨平台C++代码和单头-多个实现)Stack Overflow文章在如何为C++处理这一问题方面做了相当深入的介绍,但我觉得这些例子都不适用于这个C实现。

我不明白你为什么这么说。两个得分最高的答案中的第一个建议是对使用条件宏的想法的变体,这不仅在C中有效,而且是一种传统的方法。你自己提出了一个替代方案。

;"问题";对Arduino的支持是主要关注点,这意味着它不能用makefile魔术轻松解决。

我的意思是,平台自适应的方法必须以某种方式编码到C源代码中,而不是通过构建系统来处理。坦率地说,这是一个不寻常的约束,除非它可以通过使用感兴趣的C编译器提供的各种系统标识宏来解决。

即使你不想特别依赖makefile,你也应该考虑将一些责任归于构建系统,即使你不知道具体是什么构建系统,你也可以这样做。例如,你可以指定宏名称,如for_windows,这些名称请求为非默认平台构建。然后,您可以让构建驱动程序实例的人员根据您的构建文档来确定如何配置他们的工具,以便为他们的需求提供适当的宏定义(这通常并不难)。

我的问题是,实现这个问题的解决方案的替代方法是什么,这些方法仍然可读

如果解决方案需要完全体现在C源中,那么您有三个主要的替代方案:

  • 编写在所有平台上都能正常工作的代码,或者
  • 执行运行时检测和调整,或
  • 使用基于由支持的编译器自动定义的宏的条件编译

如果您准备依赖用户在构建时提供的宏定义,那么最后一个定义就变成了

  • 使用条件编译

不要轻易忽略第一个,但这可能是一条艰难的道路,而且对于您的特定问题来说,这可能不是完全可能的(如果您正在为独立实现编写驱动程序或其他代码,则可能不是)。

运行时自适应可以被视为代码的一种特定情况,但我想到的是一个更高级别的组织,它对主机环境执行运行时分析,并选择适合它的函数变体和内部参数,而不是在编译时做出这些选择。这是一件偶尔会做的真实事情,但对于你的特定情况来说,它可能可行,也可能不可行。

另一方面,条件编译是C中平台自适应的传统基础,并且通用形式没有其他两种形式的警告,即它可能在您的特定情况下工作,也可能不工作。通过这种方式实现的可读性和可维护性水平取决于如何实现它的细节

我提出了一些解决方案,比如在文件顶部添加每个平台的需求。[…]但这似乎是一个笨拙的解决方案。

如果你必须在你的构建中包含一个源文件,但你不希望其中的任何东西真正为目标做出贡献,那么这正是你必须做的;这可能会使调试冲突的需求成为一场噩梦";,但就这是一个真正的问题而言,我认为这与其说是语法问题,不如说是针对不同平台的不同代码计划的问题。

您还抱怨说,条件编译选项可能会给您选择开发工具带来实际困难。在我看来,你的工具和开发工作流程中应该有很好的变通方法。但是,如果您必须有一个仅基于C语言的解决方法,那么有一个(尽管很糟糕):引入一定级别的间接预处理。也就是说,将条件编译指令放在不同的源文件中,如下所示:

平台.c

#if defined(for_windows)
#include "platform_windows.c"
#else
#if defined(for_rpi)
#include "platform_rpi.c"
#else
#include "platform_arduino.c"
#endif
#endif

然后将platform.c指定为要构建的文件,但不能(直接)指定任何特定的平台文件。

这解决了您的工具演示问题,因为当您处理某个特定于平台的.c文件时,编辑器不太可能判断它是否真的包含在构建中。

请注意,包含函数实现的#include文件,或者那些不以通常指定标头的扩展名结尾的文件,被广泛认为是不好的做法。关于以上内容,我没有其他说法,但我想说,如果整个platform.c不包含其他内容,那么这是我能想到的该类别中最不糟糕的变体。

最新更新