Reputation: 954
I need to extract data from an XML and link it as a struct to a C embedded project.
The XML is unavailable in runtime. (no file system, file is too big, and security reasons). So I have to parse it and generate the struct on my PC pre-linkedge.
If I use binary data it won't necesarilly be in the same format as in the target (even with the a compiler from the same vendor, right?). Would you generate a c file to be compiled within the project? Is there an easier way?
struct myStruct s = generateMyStruct("file.xml");
s.generateCfile("convertedXml.c");
Upvotes: 0
Views: 73
Reputation: 42825
If you use the data types in <stdint.h>
(e.g. int32_t
, uint8_t
) and define fields for every byte in your struct
to keep types aligned with their sizes (e.g. 32-bit values should be 4-byte aligned, 16-bit values should be 2-byte aligned), then you should be okay and your structure's layout should match on both platforms.
For example, don't write a struct like this:
typedef struct {
uint8_t a;
uint32_t b;
} Foo;
because the compiler might magically stuff three bytes in between a
and b
so that b
is a multiple of 4 bytes from the start of the structure. Instead, put those padding bytes in yourself:
typedef struct {
uint8_t a;
uint8_t padding[3];
uint32_t b;
} Foo;
Sometimes compilers have extensions for controlling packing and alignment. Make sure both compilers have these extensions before using them. Or just ignore the extensions and manually pad data as above.
Once you've written your code that creates the structure and fills it in with data parsed from XML, then you write your function generateCfile
to generate your initialization file. It should be passed not only the output file or filename, but also a pointer to the initialized structure. (s.generateCfile("convertedXml.c");
is actually C++ syntax, not 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);
}
By simply using standard initializer syntax, you don't have to worry about byte order differences between the platforms.
You should use the generated C file with a short header file declaring the constant structure:
#ifndef InitFoo_h
#define InitFoo_h
#include "Foo.h"
extern const struct Foo kInitFoo;
#endif
BTW, there's nothing saying that a code generator has to be written in the same language as the target's compiler. I frequently write code generators in Python that output 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")
Also, higher-level languages like Python tend to have easier to use XML or JSON parsers. YMMV.
For either code generator, and given a = 15 and b = 45 in the parsed structure, you should get this in convertedXml.c
:
#include "InitFoo.h"
const struct Foo kInitFoo = {
15, { 0, 0, 0 }, 45
};
Upvotes: 1