Fully expanding header macros to get their numeric values for external use

0

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?

c
c-preprocessor
asked on Stack Overflow Sep 12, 2019 by sherrellbc • edited Sep 12, 2019 by sherrellbc

3 Answers

3
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.

answered on Stack Overflow Sep 13, 2019 by H Walters • edited Sep 13, 2019 by H Walters
0

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.

answered on Stack Overflow Sep 12, 2019 by KamilCuk • edited Sep 12, 2019 by KamilCuk
0
  1. You could write a simple C program that includes that header(s) and prints the result.

  2. You could even let it print the linker command line(s).

  3. 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.

answered on Stack Overflow Sep 12, 2019 by the busybee • edited Sep 12, 2019 by S.S. Anne

User contributions licensed under CC BY-SA 3.0