1

I have the following type definition:

typedef struct {
  int (*function)(int argc, char *argv[]);
  char *name;
} command_t;

The member function is a function pointer and the member name is a string which will store the name of the function.

To initialize a variable of type command_t, I wrote the following macro:

#define COMMAND(x) (command_t){.function = x, .name = #x}

Here's how I currently initialize an array of command_t:

int ls(int argc, char *argv[]);
int echo(int argc, char *argv[]);
int cat(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);

command_t cmd_list[] = {COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir)};

I would like to be able to initialize an array of command_t as such:

command_t cmd_list[] = COMMAND(ls, echo, cat, mkdir);

or

command_t cmd_list[] = {COMMAND(ls, echo, cat, mkdir)};

I know that COMMAND has to be a variadic macro to do so but I don't know how to write it.

2
  • 1
    I don't think it can be done with variadic macros. The preprocessor does not have the capability to deconstruct the VA_ARGS and process each arg. It can only do a simple expand of the VA_ARGS in its body. IMHO X-macros would be more appropriate here. Commented Jun 21, 2021 at 0:12
  • "I would like to be able to initialize an array of command_t as such" Why? It doesn't improve readability much, it just makes your code look more mysterious. If anything you should make a macro for the whole init list. Commented Jun 21, 2021 at 8:16

2 Answers 2

1

I think you are already close to the most readable form. Try to create the code as simple as possible. So instead of inventing some strange macro that no C programmer understands what it does without going though a jungle of macros, you could make a macro for the whole initializer list - that's fairly common practice.

That is: command_t cmd_lst[] = COMMAND_INIT; And then:

#define COMMAND(cmd) { .function = cmd, .name = #cmd }
#define COMMAND_INIT { COMMAND(ls), COMMAND(echo), COMMAND(cat), COMMAND(mkdir) }

Yet another option is X macros. They are not necessarily more readable, but could maybe lead to reduction of code repetition elsewhere:

#define FUNC_LIST(X) \
  X(ls)              \
  X(echo)            \
  X(cat)             \
  X(mkdir)           \

...

#define COMMAND_INIT(func) { .function = func, .name = #func },  
command_t cmd_lst[] = { FUNC_LIST(COMMAND_INIT) };
Sign up to request clarification or add additional context in comments.

Comments

1
//stringification of the argument Arg
#define PP_STRINGIFY(Arg) PP_STRINGIFY_(Arg)
#define PP_STRINGIFY_(Arg) #Arg

//concatenation of the two arguments
#define PP_CAT2(_1, _2) PP_CAT_(_1, _2)
#define PP_CAT_(_1, _2) _1##_2

//enumerate the number of arguments (min:1, max: 8)
#define PP_VA_NUM_ARGS(...) PP_VA_NUM_ARGS_(__VA_ARGS__,8,7,6,5,4,3,2,1)
#define PP_VA_NUM_ARGS_(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N

//a single command initializer
#define COMMAND(x) (command_t){.function = x, .name = PP_STRINGIFY(x)}

//command list initializer
#define COMMAND_LST(...) { PP_CAT2(COMMAND_LST_,PP_VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) }

//implement as many as you need
//maybe you need to extend PP_VA_NUM_ARGS (current max limit 8)
#define COMMAND_LST_1(_1) COMMAND(_1)
#define COMMAND_LST_2(_1,_2) COMMAND(_1), COMMAND(_2)
#define COMMAND_LST_3(_1,_2,_3) COMMAND_LST_2(_1,_2), COMMAND(_3)
#define COMMAND_LST_4(_1,_2,_3,_4) COMMAND_LST_3(_1,_2,_3), COMMAND(_4)

typedef struct {
    int (*function)(int argc, char *argv[]);
    char *name;
} command_t;

int ls(int argc, char *argv[]);
int echo(int argc, char *argv[]);
int cat(int argc, char *argv[]);
int mkdir(int argc, char *argv[]);

int main()
{
    command_t cmd_lst[] = COMMAND_LST(ls, echo, cat, mkdir);
    return 0;
}

CPP output via

gcc -E main.c

gives you

command_t cmd_lst[] = { 
    (command_t){.function = ls, .name = "ls"}, 
    (command_t){.function = echo, .name = "echo"}, 
    (command_t){.function = cat, .name = "cat"}, 
    (command_t){.function = mkdir, .name = "mkdir"} 
};

Comments

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.