【问题标题】:C (Embedded) : Giving source file unique ID'sC(嵌入式):为源文件提供唯一 ID
【发布时间】:2013-04-28 12:00:45
【问题描述】:

我正在开发用于嵌入式系统的紧凑型调试输出/单元测试实用程序。

我创建了一个系统,可以通过串行端口以紧凑的方式将消息输出到 PC。 为了节省内存空间/串行端口带宽,我通过为它们提供唯一的 16 位 ID,从嵌入式系统中剥离了消息字符串。

这非常简单,因为我将所有消息都放在了 1 个列表中。一些宏会将其放入枚举中:

projectdefs.h:

#define MESSAGE_TABLE(MSG) \
    MSG(HELLO, "Hello World!") \
    MSG(TEST, "Second message #ID 1") \
    MSG(TEST2, "Third message #ID 2")

messages.h:

#define MACRO_STR_CONCAT(a,b) a##b
#define MESSAGE_ENUM(codeName, str) MACRO_STR_CONCAT(MSG_, codeName)    

typedef enum messageNumbers_e {
    MESSAGE_TABLE(MESSAGE_ENUM),
    MESSAGE_COUNT
};

#define MESSAGE(codeName) messageSend(MACRO_STR_CONCAT(MSG_, codeName), __LINE__, file_number);

通过串行端口传输的唯一数据是消息 ID、行号和文件号(注意;不是字符串!)。

我遇到的问题是如何使用 C 预处理器/编译器为每个文件分配唯一 ID。我不想将每个文件名字符串存储在嵌入式程序中。这会使用(过多)串行端口上的内存或带宽。

我的想法是用宏在每个文件中定义常量 file_number。我会在每个源文件的顶部使用这个定义:

#define ASSIGN_FILENUMBER() enum { file_number = __COUNTER__ };

但是,由于每个文件都是单独编译的,这意味着 __COUNTER__ 语句在调用时始终从 0 开始,并且不知道其他文件的存在或它自己的 ID。

另一个考虑因素是编辑 MakeFile(脚本)并在其中添加文件 ID 号。但是,这会将项目构建/功能与我的 IDE 配置紧密联系在一起,这是不可取的。 此外,我不确定我当前的 IDE(Mplab X IDE 或 IAR Embedded Workbench 上的 XC16/XC32 编译器)的可能性。

我想知道是否有任何其他创造性的方法可以让标准 C 预处理器接管任务?

【问题讨论】:

  • 您的编译器是否支持预编译头文件?预编译的标头可能会保留 __COUNTER__ 值,至少这是在 MSVC 文档中编写的。
  • 如果您使用 O(1) 算法动态生成唯一 ID 会很好吗?还是我没有得到问题:o
  • 我尝试使用ASSIGN_FILENUMBER 宏手动预编译头文件。我的编译器可能是基于 gcc 的,但并不吹嘘它。我可以看到预编译的头文件被拾取(-H 输出),但是,特定于 proccesor 的定义无法编译:invalid attribute space, __sfr__, __unsafe__, etc. ignored。由于用 C 编写的嵌入式软件是完整的静态程序,因此在设备内部动态分配/计算 ID 是没有意义的。此外,跟踪主机会更困难。
  • 由于 C 预处理器不知道要编译多少文件来生成您的程序,我认为 C 预处理器将很难提供帮助。
  • 虽然不是 C 预处理器,但您可以让 makefile 运行一个 shell/python/perl 脚本,该脚本会在构建代码之前重新生成列表和常量。但更常见的选择是只转储代码地址(可能还有版本或构建纪元时间),并使用引用映射文件或类似工具链调试输出的工具将其转换回开发机器上的文件/源(您需要保留,但实际上不需要加载到目标系统上)。

标签: c embedded c-preprocessor


【解决方案1】:

我不确定在程序中存储源代码文件名对内存来说有多大意义。如果你能拥有它们,那么你可以做的一件事是声明一个像const char *fname = __FILE__; 这样的变量,然后计算某种校验和、散列或CRC,并传输这个值而不是文件名。在接收端,您可以将哈希值与其中一个文件名匹配。不过,您需要确保文件名哈希不会发生冲突。

另一种方法是使用您的 makefile 或 perl 或其他东西来维护文件计数器并将其作为宏输入 gcc,例如gcc [someparams1] -DFILENUMBER=%FILECOUNTER% somefile.c [someparams2],其中%FILECOUNTER% 是将扩展为文件计数器变量值作为文本的任何表达式。您可能需要在此处引入固定的源文件编译顺序。

您可以将这两种方法结合起来,并使用哈希而不是计数器来提供 gcc,例如-DFILENAMEHASH=%HASH%,其中%HASH% 将扩展为您的编译脚本将根据文件名生成的数字常量。

【讨论】:

  • 将字符串存储在桌面上没什么大不了的,但这个问题询问的是嵌入式应用程序,在该应用程序中通常需要/必须保持非常紧凑。
  • 我已经按照你和克里斯的建议做了。我添加了一个预构建程序(小 C# 控制台程序,但可以是任何东西..),它读取 makefile,找到所有构建命令并添加 -DFILENUMBER 参数。我也在我的调试输出程序中解析了makefile,这样我就可以知道哪个文件的文件号是哪个文件。不幸的是,没有(可靠的)C 解决方案可以解决这个问题,但这很有效并且不太麻烦。
【解决方案2】:

您是否考虑过根据您从 FILE 获得的任何信息创建一个 16 位哈希数?它可能并不完美(读取冲突),但可能足以满足您的需求。如果可以接受,您将需要一个外部查找表,以便将您的哈希编号 ID 映射到相关文件。

希望这个想法有所帮助。

【讨论】:

  • 这听起来像是一个运行时解决方案,它与将字符串排除在编译代码之外的目标不兼容。
  • 如果它可以在编译时通过某种宏来完成,这将是一个选项。但我不认为这种算术是可能的宏?我想要的是在构建/编译时生成的静态文件 ID。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-06
  • 2012-04-26
  • 2015-03-18
  • 2015-08-29
  • 1970-01-01
  • 2012-05-04
相关资源
最近更新 更多