最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Conditional compilation using the preprocessor - Stack Overflow

programmeradmin4浏览0评论

I have a list of functions generated in a Makefile and dumped into a file that looks something like:

DEF(foo)
DEF(bar)
DEF(baz)

The intent is that this file will be used like so to define a list of functions:

#define DEF(name) void name();
#include "generated_list.inc"

The complication is that I want to have a few exceptions to the generated list.
I want to do something like:

#define DONT_DEF_baz

And this would cause the name baz to just be ignored from the above.
If the preprocessor could be recursive, this would be something like:

#define DEF(name) \ 
   #ifndef DONT_DEF_ ## name \
     void name(); \
   #endif \

But of course this is not possible in C.

Is there a way to get the same effect somehow?

I have a list of functions generated in a Makefile and dumped into a file that looks something like:

DEF(foo)
DEF(bar)
DEF(baz)

The intent is that this file will be used like so to define a list of functions:

#define DEF(name) void name();
#include "generated_list.inc"

The complication is that I want to have a few exceptions to the generated list.
I want to do something like:

#define DONT_DEF_baz

And this would cause the name baz to just be ignored from the above.
If the preprocessor could be recursive, this would be something like:

#define DEF(name) \ 
   #ifndef DONT_DEF_ ## name \
     void name(); \
   #endif \

But of course this is not possible in C.

Is there a way to get the same effect somehow?

Share Improve this question asked Feb 17 at 9:51 shooshshoosh 79k57 gold badges214 silver badges331 bronze badges 3
  • Do you want do avoid declaring a function completely? Is it enough that a function with a different name is defined? – tstanisl Commented Feb 17 at 10:16
  • @tstanisl yes that's a reasonable solution as well – shoosh Commented Feb 17 at 10:19
  • 2 Where does the #define DONT_DEF_baz come from? Can't you modify the generation of the file with DEF(foo) etc to omit the line DEF(baz)? Or wrap all DEF(foo) in suitable #ifndef DONT_DEF_foo by the generation, maybe by adding a post-processing? Why is it necessary to omit the declaration of a function? – Bodo Commented Feb 17 at 10:38
Add a comment  | 

2 Answers 2

Reset to default 3

something like:

Then use something like:

// if true:
#define DONT_DEF_baz(...)
// if false:
#define DONT_DEF_baz(...)  __VA_ARGS__

And use:

#define DEF(name)  DONT_DEF_##name(void name();)

If you are up for an adventure and you want to detect undefined, we would have to complicate a bit. See https://gustedt.wordpress/2010/06/08/detect-empty-macro-arguments/ .

#define COMMA()  ,
#define EXPAND(...)  __VA_ARGS__
#define IGNORE(...)
#define IFNDEF_N2(_2,_1,N,...)  N
#define IFNDEF_N(...)           IFNDEF_N2(__VA_ARGS__)
#define IFNDEF(name, ...)       IFNDEF_N(COMMA name (), IGNORE, EXPAND)(__VA_ARGS__)

#define DEF(name)  IFNDEF(DONT_DEF_##name, void name();)

#define DONT_DEF_baz
DEF(baz)  // expands to nothing
DEF(foo)  // expands to void foo();

how it works?

  1. COMMA name () expands to COMMA DONT_DEF_baz ().
  2. When DONT_DEF_baz is defined to nothing
    1. DONT_DEF_baz is replaced to nothing
    2. COMMA name () becomes COMMA () which expands to ,
    3. so IFNDEF_N calls IFNDEF_N2(,, IGNORE, EXPAND), with arguments _2=/*nothing*/, _1=/*nothing*/, N=IGNORE, ...=EXPAND
    4. IFNDEF_N2 expands to N which is IGNORE
    5. So DEF(name) becomes IGNORE(void name();) which expands to nothing`
  3. When DONT_DEF_baz not defined
    1. COMMA name () stays as COMMA DONT_DEF_baz ().
    2. IFNDEF_N calls IFNDEF_N2(COMMA DONT_DEF_baz (), IGNORE, EXPAND), with arguments _2=COMMA DONT_DEF_baz (), _1=IGNORE, N=EXPAND
    3. IFNDEF_N2 expands to EXPAND
    4. DEF(name) becomes EXPAND(void name();) which expands to the void name();

See the linked article above and Overloading Macro on Number of Arguments .

generated in a Makefile

Maintaining complicated C preprocessor scripts in the long run might be a maintainer burden and might get unreadable. Consider just using simpler templating language, like Jinja2 or m4 or php, for code generation and then compile that code. Also, Makefile is 50 years old with syntax written for an exercise - consider CMake.

You pretty much describe "X-macros", which would be preferred "design pattern" to use here instead of cooking up some custom macro solution. Normally you'd make a plain X-macro something like this:

#define DEF(X)    \
  X(foo)          \
  X(bar)          \
  X(baz)

Which might then be used to create for example an enum or whatever:

#define DEF_ENUM(name) name,
typedef enum
{
  DEF(DEF_ENUM)
  def_n // placed here to count the number of items in the list
} def_t;

Now suppose that we also want to add an additional parameter to mark if a certain value should be used or not. There's various ways to do that, I'll use C23 __VA_OPT__ in this example. We can then tweak the above X-macro list to this:

#define DEF(X)     \
  X(foo, function) \
  X(bar, function) \
  X(baz)

Where function can be any valid pre-processor token - doesn't matter in this case.

When defining the function, we just check if there's an additional argument present or not by writing a macro where the last parameter is variadic. If there isn't a last parameter present, then __VA_OPT__ will discard whatever we placed inside it:

#define DEF_FUNCTIONS(name, ...) \
  __VA_OPT__( void name##_func (void){ puts(__func__); } )

DEF(DEF_FUNCTIONS)

Full example:

#include <stdio.h>

#define DEF(X)              \
  X(foo, function)          \
  X(bar, function)          \
  X(baz)

#define DEF_ENUM(name,...) name,
typedef enum
{
  DEF(DEF_ENUM)
  def_n
} def_t;

#define DEF_FUNCTIONS(name, ...) \
  __VA_OPT__( void name##_func (void){ puts(__func__); } )

DEF(DEF_FUNCTIONS)

int main() 
{
  puts("The enumeration constants and their values: ");
  #define PRINT_ENUM(name,...) printf("%s: %d\n", #name, name);
  DEF(PRINT_ENUM)
  printf("(There are %d of them)\n\n", def_n);

  puts("Call some functions:");
  foo_func();
  bar_func();
  //baz_func();   will not compile since it isn't defined
}

Output:

The enumeration constants and their values: 
foo: 0
bar: 1
baz: 2
(There are 3 of them)

Call some functions:
foo_func
bar_func
发布评论

评论列表(0)

  1. 暂无评论