14

I would like to do something like the following:

F_BEGIN

F(f1) {some code}
F(f2) {some code}
...
F(fn) {some code}

F_END

and have it generate the following

int f1() {some code}
int f2() {some code}
...
int fn() {some code}

int (*function_table)(void)[] = { f1, f2, ..., fn };

The functions themselves are easy. What I can't seem to do is to keep track of all of the names until the end for the function_table.

I looked at this question and this question but I couldn't get anything to work for me. Any ideas?

Community
  • 1
  • 1
No One in Particular
  • 2,846
  • 4
  • 27
  • 32

5 Answers5

20

The normal way of doing this with the preprocessor is to define all the functions in a macro that takes another macro as an argument, and then use other macros to extract what you want. For your example:

#define FUNCTION_TABLE(F) \
    F(f1, { some code }) \
    F(f2, { some code }) \
    F(f3, { some code }) \
:

    F(f99, { some code }) \
    F(f100, { some code })

#define DEFINE_FUNCTIONS(NAME, CODE)     int NAME() CODE
#define FUNCTION_NAME_LIST(NAME, CODE)   NAME,

FUNCTION_TABLE(DEFINE_FUNCTIONS)
int (*function_table)(void)[] = { FUNCTION_TABLE(FUNCTION_NAME_LIST) };
jalf
  • 243,077
  • 51
  • 345
  • 550
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • The C preprocessor doesn't allow macros to define other macros, or possess any way to do recursion or looping, so this technique (also described by ddyer) is the best you're going to be able to do. – zwol Feb 05 '11 at 03:39
  • @zwol The C preprocessor actually *can* do some cases of recursion/looping, if the preprocessor actually fully follows the standard, it just... is very non-trivial, an unintended side-effect of the expansion rules, so non-obvious that I wouldn't even bet that people would get it right while aiming to make a standard-compliant C preprocessor, and I wouldn't wish understanding/verifying such macro code on anyone. But for an example, you can check out [`tagged-union.h`](https://github.com/mentalisttraceur/c-tagged-union). – mtraceur Dec 27 '22 at 00:19
6

If you have a C99 complying compiler, the preprocessor has variable length argument lists. P99 has a preprocessor P99_FOR that can do "code unrolling" like the one you want to achieve. To stay close to your example

#define MYFUNC(DUMMY, FN, I) int FN(void) { return I; } 
#define GENFUNCS(...)                                          \
P99_FOR(, P99_NARG(__VA_ARGS__), P00_IGN, MYFUNC, __VA_ARGS__) \
int (*function_table)(void)[] = { __VA_ARGS__ }

GENFUNCS(toto, hui, gogo);

would expand to the following (untested)

int toto(void) { return 0; } 
int hui(void) { return 1; }
int gogo(void) { return 2; }
int (*function_table)(void)[] = { toto, hui, gogo };
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
2

This is sort of abuse of CPP but a common type of abuse. I handle situations like this by defining dummy macros

#define FUNCTIONS \
 foo(a,b,c,d) \
 foo(a,b,c,d) \
 foo(a,b,c,d)

now, 

#define foo(a,b,c,d) \
 a+b ;

FUNCTIONS

#undef foo

later, when you want something different done with the same list

#define foo(a,b,c,d) \
 a: c+d ;

FUNCTIONS

#undef foo

It's a bit ugly and cumbersome, but it works.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
ddyer
  • 1,792
  • 19
  • 26
2

There's this thing called X Macro which is used as:

a technique for reliable maintenance of parallel lists, of code or data, whose corresponding items must appear in the same order

This is how it works:

    #include <stdio.h>

//you create macro that contains your values and place them in (yet) not defined macro
#define COLORS\
    X(red, 91)\
    X(green, 92)\
    X(blue, 94)\

//you can name that macro however you like but conventional way is just an "X"

//and then you will be able to define a format for your values in that macro
#define X(name, value) name = value,
typedef enum { COLORS } Color;
#undef X //so you redefine it below

int main(void)
{
    #define X(name, value) printf("%d, ", name);
    COLORS
    #undef X
    return 0;
}

Solution for your problem would be:

#define FUNCTIONS \
F(f1, code1)\
F(f2, code2)\
F(f3, code3)

#define F(name, code) int name(void){code}
FUNCTIONS
#undef F


#define F(name, code) &name,
int (*function_table[])(void) = { FUNCTIONS };
#undef F
Christoffer Bubach
  • 1,676
  • 3
  • 17
  • 45
WENDYN
  • 650
  • 7
  • 15
0

Boost is a C++ library, but it's Preprocessor module should still be good for use in C. It offers some surprisingly advanced data types and functionality for use in the preprocessor. You could check it out.

Puppy
  • 144,682
  • 38
  • 256
  • 465