0

Simple idea:

I'm using X-macros to define command list structure and declare command callbacks.

#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#define COMMAND_LIST(X) \
        X(toto_all) \
        X(help) \
        //end of list

typedef void (*callback_t)(int a, int b);

typedef struct 
{
    char * name;    
    callback_t callback;
}command_t;


#define CALLBACK_DEC(COMMAND_NAME)  void _##COMMAND_NAME(int a, int b);
COMMAND_LIST(CALLBACK_DEC)

#define COMMAND_DEF(COMMAND_NAME)  { #COMMAND_NAME, & _##COMMAND_NAME },
static command_t commands[] =
{
  COMMAND_LIST(COMMAND_DEF)
};

#define COMMAND(COMMAND_NAME,CODE)          void _##COMMAND_NAME(int A, int B) {  CODE  }

COMMAND(toto_all,
    printf("helloworld\n");
)

COMMAND(help,
    printf("help!\n");
)

int main()
{
    commands[0].callback(1,2);
    commands[1].callback(1,2);

    return 0;
}

it works.

helloworld
help!


Adding some parameters:

If you change the first command list to this (by adding parameters)

#define COMMAND_LIST(X) \
        X(toto_all,         1,      3,      5) \
        X(help,             0,      0,      0) \
        //end of list

typedef struct 
{
    callback_t callback;
    char * name;  
    int arg_min; 
    int arg_max;
    int arg_num;
}command_t;

then, when running it I get the following error:

macro "CALLBACK_DEC" passed 4 arguments, but takes just 1

I have to use all the parameters for the command list definition (command declaration):

#define COMMAND_DEF(COMMAND_NAME, ARG_MIN, ARG_MAX, ARG_MAX, ARG_NUM)  (command_t){ #COMMAND_NAME, & _##COMMAND_NAME, ARG_MIN, ARG_MAX, ARG_NUM},

but it's quite tricky to now use it for the callback declaration...

Is there a clever way for this X-macro to avoid this error?


I thought about the non-macro way to mask unused parameters:

which is by using (void)param;, which gives the ugly

#define CALLBACK_DEC(COMMAND_NAME, ARG_MIN, ARG_MAX, ARG_NUM) void _##COMMAND_NAME(int a, int b); void(ARG_MIN); void(ARG_MAX);  void(ARG_NUM)

and this does not work...I get a strange:

main.c:27:20: error: expected identifier or ‘(’ before numeric constant
         X(toto_all,0,0,0) \

I think there is another way:

maybe using something like this...

#define COMMAND_LIST(X,Y) \
        X(Y(toto_all,         0,      0,      0)) \
        X(Y(help,             0,      0,      0)) \
        //command name,    arg min, arg max, arg num, string?
        //end of list

typedef void (*callback_t)(int a, int b);

typedef struct 
{
    char * name;    
    callback_t callback;
}command_t;

#define GET_ONLY_NAME(COMMAND_NAME1, ARG_MIN, ARG_MAX, ARG_NUM) COMMAND_NAME1
#define CALLBACK_DEC(COMMAND_NAME)  void _##COMMAND_NAME(int a, int b);
COMMAND_LIST(CALLBACK_DEC,GET_ONLY_NAME);
#undef CALLBACK_DEC

#define GET_FULL_LIST(X) X
#define COMMAND_DEF(COMMAND_NAME, ARG_MIN, ARG_MAX, ARG_NUM)  (command_t){ #COMMAND_NAME, & _##COMMAND_NAME, ARG_MIN, ARG_MAX, ARG_NUM},
static command_t commands[] =
{
  COMMAND_LIST(COMMAND_DEF,GET_FULL_LIST)
};
#undef COMMAND_DEF

but I still get the following strange error, there is a problem in the expansion but i can't see where...

main.c:27:31: error: expected ‘)’ before numeric constant
         X(Y(toto_all,         0,      0,      0)) \

Maybe the truth is elsewhere... :)

any hints?

Guillaume D
  • 1,958
  • 2
  • 6
  • 30
  • The "it works" version doesn't work. You have a semicolon here: `COMMAND_LIST(CALLBACK_DEC);` but also in the macro declaration above it. Furthermore, you can't use compound literals to initialize a file scope variable since that's not a constant expression. (Just drop the compound literal notation and it works.) Which compiler and what non-standard setup is required to compile the code? – Lundin Feb 24 '20 at 11:28
  • just copy paste the first code and you'll see it works: https://onlinegdb.com/SyABNE-NL – Guillaume D Feb 24 '20 at 11:32
  • 1
    Ok so default GNU C. Might want to make a habit of compiling with `-std=c11 -pedantic-errors` and you'll find various bugs and syntax errors that the GNU C version hides. – Lundin Feb 24 '20 at 11:34
  • thanx, corrected in the post. This does not answer the question though :o) – Guillaume D Feb 24 '20 at 11:36

1 Answers1

1

This is a an issue with X macros overall - you have to write a macro accepting all parameters, even when you are just using a few.

In your case you pass the specific macro as a parameter to the list, so you can add some flexibility there. Using variadic macros might solve the problem. You should be able to do like this:

#define COMMAND_DEF(COMMAND_NAME, ...)  { #COMMAND_NAME, & _##COMMAND_NAME },
...
COMMAND_LIST(COMMAND_DEF)

Where you only explicitly name the parameters that this particular macro is interested in, then let the rest of them go into the ... part which is then ignored.

This does however build in a dependency in the data, because it only allows you to expand parameters from left to right, so to speak. So for

X(toto_all,         1,      3,      5,      "-")

you can write a macro that uses just toto_all, or toto_all and 1, but you would't be able write a macro that just uses for example 1 and 3. For such special cases I believe you will still have to name all macro parameters.

Yet another option is self-documenting code:

#define COMMAND_DEF(COMMAND_NAME, ignored1, FOO, ignored2, ignored3) \
/* do stuff with COMMAND NAME and FOO only */
Lundin
  • 155,020
  • 33
  • 213
  • 341