1

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?

3
  • Do you want do avoid declaring a function completely? Is it enough that a function with a different name is defined? Commented Feb 17 at 10:16
  • @tstanisl yes that's a reasonable solution as well 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? Commented Feb 17 at 10:38

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

2 Comments

This seems to do the opposite actually. foo() is undecraled, baz is declared. In any case, do you mind explaining how it works?
hi @shosh fixed and added explanation
-1

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

3 Comments

This doesn't fulfill the requirement that the generator of the DEF() lines doesn't know about which lines should be excluded.
@shoosh That "requirement" is not really clear from the question, is it?
@shoosh However I believe that is easy enough to do with some #ifdef directives before the X-macro list.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.