在 PC 中为嵌入式目标构建 C 结构
Building in PC a C struct for an embedded target
我需要从 XML 和 link 中提取数据作为 C 嵌入式项目的结构。
XML 在运行时不可用。 (没有文件系统,文件太大,和安全原因)。所以我必须解析它并在我的 PC pre-linkedge.
上生成结构
如果我使用二进制数据,它不一定与目标中的格式相同(即使使用来自同一供应商的编译器,对吧?)。您会生成要在项目中编译的 c 文件吗?有没有更简单的方法?
struct myStruct s = generateMyStruct("file.xml");
s.generateCfile("convertedXml.c");
控制结构布局
如果您使用 <stdint.h>
中的数据类型(例如 int32_t
、uint8_t
)并为 struct
中的每个字节定义字段以保持类型与它们的对齐大小(例如 32 位值应该是 4 字节对齐,16 位值应该是 2 字节对齐),那么你应该没问题,你的结构布局应该在两个平台上匹配。
例如,不要写这样的结构:
typedef struct {
uint8_t a;
uint32_t b;
} Foo;
因为编译器可能会神奇地在 a
和 b
之间填充三个字节,因此 b
是从结构开始算起的 4 个字节的倍数。相反,将这些填充字节放入您自己:
typedef struct {
uint8_t a;
uint8_t padding[3];
uint32_t b;
} Foo;
有时编译器有控制打包和对齐的扩展。在使用它们之前确保两个编译器都有这些扩展。或者只是忽略扩展名并像上面那样手动填充数据。
正在生成 C 初始化代码
一旦您编写了创建结构的代码并用从 XML 解析的数据填充它,那么您就可以编写函数 generateCfile
来生成初始化文件。它不仅应该传递输出文件或文件名,还应该传递指向初始化结构的指针。 (s.generateCfile("convertedXml.c");
实际上是 C++ 语法,不是 C。)
void generateCfile(const struct Foo* s, const char* filename) {
FILE* fp = fopen(filename, "w");
fprintf(fp, "#include \"InitFoo.h\"\n\n");
fprintf(fp, "const struct Foo kInitFoo = {\n");
fprintf(fp, " %u, { 0, 0, 0 }, %u\n", s->a, s->b);
fprintf(fp, "};\n");
fclose(fp);
}
通过简单地使用标准初始化语法,您不必担心平台之间的字节顺序差异。
您应该将生成的 C 文件与声明常量结构的短头文件一起使用:
#ifndef InitFoo_h
#define InitFoo_h
#include "Foo.h"
extern const struct Foo kInitFoo;
#endif
顺便说一句,代码生成器必须使用与目标编译器相同的语言来编写。我经常在 Python 中编写输出 C++ 的代码生成器。
# Remember to double braces that should be in the output.
TEMPLATE = '''
#include "InitFoo.h"
const struct Foo kInitFoo = {{
{a:d}, {{ 0, 0, 0 }}, {b:d}
}};
'''
def generateCfile(foo, filename):
with open(filename, "w") as f:
f.write(TEMPLATE.format(**foo));
# test code
generateCfile({"a": 15, "b": 45}, "convertedXml.c")
此外,像 Python 这样的高级语言往往更易于使用 XML 或 JSON 解析器。 YMMV.
对于任一代码生成器,在解析结构中给定 a = 15 和 b = 45,您应该在 convertedXml.c
:
中得到它
#include "InitFoo.h"
const struct Foo kInitFoo = {
15, { 0, 0, 0 }, 45
};
我需要从 XML 和 link 中提取数据作为 C 嵌入式项目的结构。
XML 在运行时不可用。 (没有文件系统,文件太大,和安全原因)。所以我必须解析它并在我的 PC pre-linkedge.
上生成结构如果我使用二进制数据,它不一定与目标中的格式相同(即使使用来自同一供应商的编译器,对吧?)。您会生成要在项目中编译的 c 文件吗?有没有更简单的方法?
struct myStruct s = generateMyStruct("file.xml");
s.generateCfile("convertedXml.c");
控制结构布局
如果您使用 <stdint.h>
中的数据类型(例如 int32_t
、uint8_t
)并为 struct
中的每个字节定义字段以保持类型与它们的对齐大小(例如 32 位值应该是 4 字节对齐,16 位值应该是 2 字节对齐),那么你应该没问题,你的结构布局应该在两个平台上匹配。
例如,不要写这样的结构:
typedef struct {
uint8_t a;
uint32_t b;
} Foo;
因为编译器可能会神奇地在 a
和 b
之间填充三个字节,因此 b
是从结构开始算起的 4 个字节的倍数。相反,将这些填充字节放入您自己:
typedef struct {
uint8_t a;
uint8_t padding[3];
uint32_t b;
} Foo;
有时编译器有控制打包和对齐的扩展。在使用它们之前确保两个编译器都有这些扩展。或者只是忽略扩展名并像上面那样手动填充数据。
正在生成 C 初始化代码
一旦您编写了创建结构的代码并用从 XML 解析的数据填充它,那么您就可以编写函数 generateCfile
来生成初始化文件。它不仅应该传递输出文件或文件名,还应该传递指向初始化结构的指针。 (s.generateCfile("convertedXml.c");
实际上是 C++ 语法,不是 C。)
void generateCfile(const struct Foo* s, const char* filename) {
FILE* fp = fopen(filename, "w");
fprintf(fp, "#include \"InitFoo.h\"\n\n");
fprintf(fp, "const struct Foo kInitFoo = {\n");
fprintf(fp, " %u, { 0, 0, 0 }, %u\n", s->a, s->b);
fprintf(fp, "};\n");
fclose(fp);
}
通过简单地使用标准初始化语法,您不必担心平台之间的字节顺序差异。
您应该将生成的 C 文件与声明常量结构的短头文件一起使用:
#ifndef InitFoo_h
#define InitFoo_h
#include "Foo.h"
extern const struct Foo kInitFoo;
#endif
顺便说一句,代码生成器必须使用与目标编译器相同的语言来编写。我经常在 Python 中编写输出 C++ 的代码生成器。
# Remember to double braces that should be in the output.
TEMPLATE = '''
#include "InitFoo.h"
const struct Foo kInitFoo = {{
{a:d}, {{ 0, 0, 0 }}, {b:d}
}};
'''
def generateCfile(foo, filename):
with open(filename, "w") as f:
f.write(TEMPLATE.format(**foo));
# test code
generateCfile({"a": 15, "b": 45}, "convertedXml.c")
此外,像 Python 这样的高级语言往往更易于使用 XML 或 JSON 解析器。 YMMV.
对于任一代码生成器,在解析结构中给定 a = 15 和 b = 45,您应该在 convertedXml.c
:
#include "InitFoo.h"
const struct Foo kInitFoo = {
15, { 0, 0, 0 }, 45
};