I have a complicated header file with lots of dependencies on other earlier-defined macros that layout the memory map of an embedded system. For example:
#define RAM_BASE (0x40000000)
#define RAM_SIZE (0x10000000)
#define SECURE_RAM_SIZE (0x00200000)
#define SECURE_RAM_BASE (RAM_BASE + RAM_SIZE - SECURE_RAM_SIZE)
For many reasons I need the result of SECURE_RAM_BASE
outside of the compiled program. So, my thought was to use the C preprocessor to expand these macros and awk
the result as needed. However, the macro expansion with cpp -dD <file>
was exactly as shown above (less extra white space).
I was expecting something like this:
RAM_BASE 0x40000000
RAM_SIZE 0x10000000
SECURE_RAM_SIZE 0x00200000
SECURE_RAM_BASE 0x4fe00000
But it seems (at least as far as I interpret the man page) that such expansion may only be accessible when the macros are actually used in code. Even still, the following code-use:
printf("Base: 0x%x\n", SECURE_RAM_BASE);
Expands to:
printf("Base: 0x%x\n", ((0x40000000) + (0x10000000) - (0x00200000)));
Is there a way to produce a 'fully-computed' expansion result using the C preprocessor?
Is there a way to produce a 'fully-computed' expansion result using the C preprocessor?
Why, yes, there is. (Incidentally, boost preprocessor solves a similar problem using its concept of slots; but the solution here is more tailored to this use case and hex outputs). Comments to your question already lay the ground rules... C preprocessor macro expansion cannot evaluate expressions. But C preprocessor conditional directives can. Conditional directives don't reduce expressions, but with a bit of work you can get the CPP to coax the results out. Since your goal is to simply get results out, you're not actually limited by macro evaluation.
High level approach
Given those constraints, you want a file devoted to evaluating and printing an expression (let's say it's called print_expression.h
). The evaluation should be performed by an include directive; i.e., #include "print_expression.h"
; this way, we can use the one CPP tool capable of evaluating expressions (viz, conditional directives) to do so. We can simply have this file evaluate the expression EXPRESSION
; you can #define
this before the include. Since you're going to reuse this for multiple macros that expand to expressions, we may want to preface the evaluation result with EXPRESSION_LABEL
, which you can define as something. Since this is a preprocessor program rather than a "normal" header, it can helpfully clean up after itself and skip inclusion guards so you can immediately reuse it.
Driving the solution
So for now ignore the details, and assume this just works... to generate something akin to the outputs you want on your sample header, you would include the header, and then need to pump this utility as follows:
#define EXPRESSION_LABEL 8RAM_BASE
#define EXPRESSION RAM_BASE
#include "print_expression.h"
#define EXPRESSION_LABEL 8RAM_SIZE
#define EXPRESSION RAM_SIZE
#include "print_expression.h"
...and so on. But you don't need this file (assuming your cpp takes stdin); you mentioned awk, so assuming you also have sed and bash (and maybe a gnu like -P
flag to your cpp to strip #line
directives and clutter):
(echo '#include "complicated_header.h"' ;
echo RAM_BASE,RAM_SIZE,SECURE_RAM_BASE,SECURE_RAM_SIZE | \
sed -e 's/,/\n/g' -e 's/.*/#define EXPRESSION_LABEL 8&\n#define EXPRESSION &\n#include "print_expression.h"/g' ) | cpp -E -P
Something like this is probably what you want to do, since from your question it sounds like you're going to do more processing on the outputs as well given specific extracted evaluated values. Note that I'm prepending a 8
to the expression labels; this in CPP-ese makes it a "pp-number" that can't possibly evaluate... you can strip it out on the output (maybe with a cut -c 2-
).
The reusable calculator (by description)
To make print_expression.h
isn't too complicated, but it's going to be a big file, so I'll just outline the concept rather than inline it here (but see below). I'll assume you want your output to be an 8 nibble hex number in hex format. What you want to do then is to define a macro for each nibble in this hex number, to be pasted together when producing the output; the definition of each nibble macro will be given by evaluating a #if/#elif/#else/#endif
chain that specifically checks the value for that nibble. To make this a bit easier and repetitive (so you can copy/paste/replace it into being), you can have a helper macro evaluate EXPRESSION
and shift the nibble in. So to get you started, your file looks something like this:
#define RESULT_NIBBLE(NDX_) (((EXPRESSION)>>(NDX_*4))&0xF)
#if RESULT_NIBBLE(7)==0xF
#define RESULT_NIBBLE_7 F
#elif RESULT_NIBBLE(7)==0xE
#define RESULT_NIBBLE_7 E
#elif RESULT_NIBBLE(7)==0xD
#define RESULT_NIBBLE_7 D
...
#elif RESULT_NIBBLE(7)==0x1)
#define RESULT_NIBBLE_7 1
#else
#define RESULT_NIBBLE_7 0
#endif
Following this, create a chain to define RESULT_NIBBLE_6
down to RESULT_NIBBLE_0
. Once you finally get to the end, you just need to paste all of this onto 0x
with an indirect paste, dump the results by invoking the macros, then clean up to make yourself ready for the next usage:
#define HEXRESULT(A,B,C,D,E,F,G,H) HEXRESULTI(A,B,C,D,E,F,G,H)
#define HEXRESULTI(A,B,C,D,E,F,G,H) 0x ## A ## B ## C ## D ## E ## F ## G ## H
EXPRESSION_LABEL HEXRESULT(RESULT_NIBBLE_7,RESULT_NIBBLE_6,RESULT_NIBBLE_5,RESULT_NIBBLE_4,RESULT_NIBBLE_3,RESULT_NIBBLE_2,RESULT_NIBBLE_1,RESULT_NIBBLE_0)
#undef HEXRESULTI
#undef HEXRESULT
#undef RESULT_NIBBLE_7
#undef RESULT_NIBBLE_6
#undef RESULT_NIBBLE_5
#undef RESULT_NIBBLE_4
#undef RESULT_NIBBLE_3
#undef RESULT_NIBBLE_2
#undef RESULT_NIBBLE_1
#undef RESULT_NIBBLE_0
#undef EXPRESSION
#undef EXPRESSION_LABEL
Brief demo
This demo emulates the solution good enough for single-file online demonstration. Note that lines 27-317
effectively is a viable working print_expression.h
in full.
Is there a way to produce a 'fully-computed' expansion result using the C preprocessor?
No it is not possible. The C preprocessor only calculates anything in expressions after #if
and #elif
. In simple terms, the C preprocessor works in most cases as a string replacement. It sees SECURE_RAM_BASE
it substitutes it for (RAM_BASE + RAM_SIZE - SECURE_RAM_SIZE)
and the substitutes it for ((0x40000000) + (0x10000000) - (0x00200000))
. No calculation is done except in #if
and #endif
, only string substitution.
That said..., obviously you can:
#define RAM_BASE (0x40000000)
#define RAM_SIZE (0x10000000)
#define SECURE_RAM_SIZE (0x00200000)
#define SECURE_RAM_BASE_CALC (RAM_BASE + RAM_SIZE - SECURE_RAM_SIZE)
#if SECURE_RAM_BASE_CALC == 1
#define SECURE_RAM_BASE 1
#elif SECURE_RAM_BASE_CALC == 2
#define SECURE_RAM_BASE 2
#elif SECURE_RAM_BASE_CALC == 3
#define SECURE_RAM_BASE 3
// ...
// ca. about 0x4fe00000 * 2 = 2680160256 =~ 2 bilion! more lines...
// ...
#elif SECURE_RAM_BASE_CALC == 0x4fe00000
#define SECURE_RAM_BASE 0x4fe00000
#endif
But this is way overkill.
You could write a simple C program that includes that header(s) and prints the result.
You could even let it print the linker command line(s).
Finally you could let this tiny program execute the command(s).
Now it's up to you with what stage you'll be most happy.
User contributions licensed under CC BY-SA 3.0