4

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Roman A. Taycher
  • 18,619
  • 19
  • 86
  • 141
  • No as I mentioned gcc -E does file inclusion. I want a result file that is usable after auto reformatting. – Roman A. Taycher Jun 15 '11 at 05:39
  • 1
    I mean, you wrote `gcc -F` in the question, I think you meant `gcc -E`...:-) – littleadv Jun 15 '11 at 05:43
  • About how much a macro can be replace a function, it was discussed here: [Macros faking functions](http://stackoverflow.com/questions/18424458/how-much-is-it-possible-to-create-fake-functions-with-macros-in-c/18424464#18424464) – pablo1977 Aug 27 '13 at 14:35

4 Answers4

3

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

Jim Balter
  • 16,163
  • 3
  • 43
  • 66
2

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

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • This doesn't work because it expands all macros defined in the header files, not just the local macros. There's a simpler approach ... see my answer. – Jim Balter Jun 15 '11 at 08:05
  • @Jim Hmmm... I would assume the macros be defined in a header file, but then again - you can just delete the `#include` lines altogether, and put them back after you're done, and get the same result, without expanding the unneeded macros. I personally don't like command line parameters, the less of those the better, you have to edit the files after running the script anyway, so doesn't really matter. – littleadv Jun 15 '11 at 08:10
  • @littleadv It's much much easier to just process the text to hide the `#`s than to delete lines and later put them back. "I personally don't like command line parameters" -- You need the parameters to get the preprocessor to do the right thing, e.g., not delete comments and not insert line directives, and such personal dislikes simply get in the way of solving problems and are unprofessional. – Jim Balter Jun 15 '11 at 08:24
  • @Jim, you have a valid point, personal dislikes, if affect the results, are bad. However, I can argue that ignoring coding standards is even worse idea, yet you have no problem helping the OP with that:-) Anyway, your answer is perfectly valid, of course. – littleadv Jun 15 '11 at 08:27
  • "I would assume the macros be defined in a header file" -- um, your own example includes them locally. Anyway, my solution (in its latest incarnation) allows doing it either way. "you have to edit the files after running the script anyway" -- no, you don't. The script could be invoked from a Makefile and the whole thing would run automatically. – Jim Balter Jun 15 '11 at 08:29
  • @littleadv Good thing you put that smiley there, because otherwise that would be a really silly comment. All I'm doing is answering the OP's question, not expressing a judgment. Were I to do that, I would note that the "coding standards" in this case are the same sort of unprofessional dislike affecting the result -- macros are a valid and useful C tool, and making developers jump through hoops to use them is poor management. – Jim Balter Jun 15 '11 at 08:35
  • @Jim, ok, fine, I really don't understand what are you trying to prove here. That your answer is better? Let the community and the OP decide, I'm sure they'll vote fairly. I voted on your answer as well, you want me to copy it to mine? – littleadv Jun 15 '11 at 08:35
  • "The script could be invoked from a Makefile" -- hmmm, that would be pointless, wouldn't it? The OP wants to transform code that violates the project's standards into code that doesn't, then check it in. That means maintaining shadow source, which is a really bad idea and will go out of sync if anyone else makes changes. So the very idea of doing this isn't a good one, and the OP should just buckle down and follow project guidelines (or quit and go to work for more intelligent people). – Jim Balter Jun 15 '11 at 08:40
  • @littleadv That comment is extremely immature and ego-defensive. I'm not trying to prove anything, I'm merely expressing views. Sheesh. End of discussion for me. – Jim Balter Jun 15 '11 at 08:42
1

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.

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • +1 for extracting the meat out of our exchange in the other thread and turning it into a solid answer ... well done ... and going beyond the problem as stated, to the meta-issue ... an example of the X-Y problem (http://www.google.com/search?q=X-Y+problem). – Jim Balter Jun 15 '11 at 10:24
  • a lot of these are not constants or things easily implemented in c functions. Its pretty hard to add code before/after function calls with arbitrary number of arguments in c except with macros(using the #define M(f,a) ... ret_val = func args... "M(func_x,(argy,argz))" expands to "func_x (argy,argz)" trick – Roman A. Taycher Jun 22 '11 at 07:20
0

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 )
    ;  
 }      
Community
  • 1
  • 1
pablo1977
  • 4,281
  • 1
  • 15
  • 41