Roman A. Taycher
Roman A. Taycher

Reputation: 19477

Is there a way to substitute C macros for normal C code

I want to run tool x on a C file and get the post-macro code. (If we can do only function like macros even better). I know about gcc -E but that does all the includes into one big file as well.

Basically I'd like to use some C macros for repetitive code but don't want the final code to contain any macros because they are frowned upon by the project.

Upvotes: 4

Views: 1268

Answers (4)

pablo1977
pablo1977

Reputation: 4433

As a possible solution to your problem: "write a macro and then discard it, by replacing by an equivalent function", you can use prototyped function-like macros. They have some limitations, and must be used with some care. But they work almost the same way as a function.

#define xxPseudoPrototype(RETTYPE, MACRODATA, ARGS) typedef struct { RETTYPE xxmacro__ret__; ARGS } MACRODATA

xxPseudoPrototype(float, xxSUM_data, int x; float y; );
xxSUM_data xxsum;
#define SUM_intfloat(X, Y) ( xxsum = (xxSUM_data){ .x = (X), .y = (Y) }, \
    xxsum.xxmacro__ret__ = xxsum.x + xxsum.y, \
    xxsum.xxmacro__ret__)

I have explained the details here (mainly section 4, for function-like macros):

Macros faking functions

  • The 1st line defines a macro that can be used to declare pseudoprototypes for macros.
  • The 2nd line uses that macro providing such a pseudoprototype. It defines "formal" parameters of the desired types. It contains, in order, the "return" type desired for the macro, the name of a struct where the parameters of the macro will be held, and finally the parameters (with types!) of the macro. I prefer call them pseudoparameters.
  • The 3rd line is an obligatory statement, which makes "real" the pseudoparameters. It declares a struct, that it is necessary to write. It defines a struct containing the "real" version of the pseudoparameters.
  • Finally, the macro itself it is defined, as a chained list of expressiones, separated by comma operators. The first operand is used to "load" the arguments of the macro into the "real" typed parameters. The last operand is the "returning value", which has also the desired type.

Observe that the compiler does right and transparent diagnostics of types.
(However, it is necessary to have some care with these constructs, as explained in the link).

Now, if you can collect all the sentences of your macro as a chain of function-calls separated by commas, then you can obtain function-like macro, as you desired.

Moreover, you can easily convert it into a real function, since the list of parameters is already defined. The type-checking was already done, so everything will work fine. In the example above, you have to replace all the lines (except the first), by these other ones:

#define xxPseudoPrototype(RETTYPE, MACRODATA, ARGS) typedef struct { RETTYPE xxmacro__ret__; ARGS } MACRODATA

float SUM_intfloat(int x, float y) {                       /* (1) */
   xxPseudoPrototype(float, xxSUM_data, int x; float y; ); /* (2) */
   xxSUM_data xxsum;                                       /* (2) */
   return                                                  /* (3) */
     ( xxsum = (xxSUM_data){ .x = x, .y = y },             /* (4) (5) (6) */
         xxsum.xxmacro__ret__ = xxsum.x + xxsum.y,         /* (5) (6) */
         xxsum.xxmacro__ret__)                             /* (6) */ 
    ;                                                      /* (7) */
 }                                                         /* (8) */

The replacement will follow a sistematic procedure:

(1) Macro header turned into function header. Semicolons (;) are replaced by commas (,).
(2) Declaration lines are moved inside function body.
(3) The "return" word is added.
(4) Macro arguments X, Y, are replaced by function parameters x, y.
(5) All ending "\"s are removed.
(6) All intermediante calculations and function calls are left untouched.
(7) Semicolon added.
(8) Closing function body.

Problem: Although this approach solves your needs, note that the function has duplicated its list of parameters. This is not good: pseudoprototypes and duplicates have to be erased:

float SUM_intfloat(int x, float y) { 
   return  
     ( x + y )
    ;  
 }      

Upvotes: 0

littleadv
littleadv

Reputation: 20272

I decided to add another answer, because it's entirely different.

Instead of having tricks to expand macros into the project source repository - have you considered using const variables and inline functions as alternative?

Basically those are the reasons that the macros are "frown upon" in your project.

You have to keep in mind that inline is merely a "suggestion" (i.e.: the function might not be in fact inlined) and const will use memory instead of being a constant literal (well, depends on compiler, good compiler will optimize), but that will do two things:

  1. Keep your code in adherence to the project coding standards (which is always a good thing, at least politically if not necessarily technically)
  2. Will not require additional hidden scripts or actions on your behalf to keep the code reusable and maintainable (I'm assuming you want to use macros in order to avoid repetitive code, right?)

So keep that in mind as well, as an option.

Upvotes: 1

Jim Balter
Jim Balter

Reputation: 16406

Using the scripting language of your choice, comment out all #includes, then run gcc -E -Wp,-P,-C,-CC foo.c then uncomment the #includes. Or you could replace #include with some string that doesn't start with a # ... e.g., include# or @include; the possibilities are endless. The approach of using @ instead of # gives you complete control over which preprocessor directives do and don't get expanded ... code the ones you don't want expanded with @, and then the script just runs gcc -E and then changes @ to #. However, I think it would be better to do it the other way around, using a special marker (e.g., @) to indicate your expandable macros. Then the script would turn leading #s into something else (e.g., HIDE#) and turn the marker (@, for instance) into #, run gcc -E, then turn HIDE# (or whatever) back into #.

-Wp specifies preprocessor options. -P means don't generate line directives, -C means don't delete comments, and -CC means don't delete comments generated by macros -- that means that comments in your code-generating macros will be preserved in the output. To determine all available preprocessor options (there are a great many, mostly not of interest), run gcc -Wp,--help anyfile.c ... that's what I did to come up with this answer (after first running gcc --help to find the -Wp option). (Knowing how to find things out is more important than knowing things.)

Upvotes: 3

littleadv
littleadv

Reputation: 20272

How about putting a delimiter in your code right after the #include list, so that you could get rid of the include files expansions manually, but have the macro expansion intact after running gcc -E?

Something like:

#include <one>
#include <two>
void delete_everything_above_and_put_includes_back(); // delimeter
#define MACRO(X) ...
//rest of the code

I'm not aware of a tool that expands macros but doesn't expand #includes...

Upvotes: 2

Related Questions