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 |2 Answers
Reset to default 3something 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?
COMMA name ()
expands toCOMMA DONT_DEF_baz ()
.- When
DONT_DEF_baz
is defined to nothingDONT_DEF_baz
is replaced to nothingCOMMA name ()
becomesCOMMA ()
which expands to,
- so
IFNDEF_N
callsIFNDEF_N2(,, IGNORE, EXPAND)
, with arguments_2=/*nothing*/, _1=/*nothing*/, N=IGNORE, ...=EXPAND
IFNDEF_N2
expands toN
which isIGNORE
- So
DEF(name)
becomesIGNORE(void name();)
which expands to nothing`
- When
DONT_DEF_baz
not definedCOMMA name ()
stays asCOMMA DONT_DEF_baz ()
.IFNDEF_N
callsIFNDEF_N2(COMMA DONT_DEF_baz (), IGNORE, EXPAND)
, with arguments_2=COMMA DONT_DEF_baz (), _1=IGNORE, N=EXPAND
- IFNDEF_N2 expands to EXPAND
DEF(name)
becomesEXPAND(void name();)
which expands to thevoid 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
#define DONT_DEF_baz
come from? Can't you modify the generation of the file withDEF(foo)
etc to omit the lineDEF(baz)
? Or wrap allDEF(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