0

I am new to C, so sorry if this problem turns trivial. I have written a item structure initialization using macros (for now) which takes in a method and an array of strings to be used as the method arguments. Initialization of the last argument is throwing warning about excess of elements in scalar initializer.

The item is a little structure holding name, method, and method arguments. As you can see, the last field is declared to be an array of strings.

// Definition of mcl_item; aka. mitem
struct mcl_item {
    char *name;
    void (*method)(char *args[]);
    char **args;
};
#define mitem struct mcl_item

// Create a new mitem (user will use item and item0 macros instead)
mitem new_mitem(char name[MAX_STRLEN], void (*method)(char **), int n, char *arg, ...) {
    // return simple mitem without arguments allocation
    return((mitem){
        .name = name,
        .method = method,
        .args = 0
    });
}

This is how my macros looks like, here also it looks for me that the scalar initialization should work:

// Define arguments amount by a macro
#define ARGSN(...) (int)(sizeof((char *){__VA_ARGS__})/sizeof(char *))
// Overload new_mitem() to avoid taking n parameter storing generated size
#define NEW_MITEM(name, method, ...) \
    new_mitem(name, method, ARGSN(__VA_ARGS__), (char *){__VA_ARGS__})
// Top level macro for creating item with arguments
#define item(name, method, ...) NEW_MITEM(name, method, __VA_ARGS__)
// Top level macro for creating item without arguments
#define item0(name, method) NEW_MITEM(name, method, 0)

And then inside of the main procedure I initialize the items and access them.

// Boilerplate methods used by items
void hello(char *args[]);
void extend(char *args[]);

int main() {
    // initialize items
    mitem item1 = item("extend", &extend, "Charlie", "Delta");
    mitem item2 = item0("hello", &hello);
    // access item1
    printf("Accessing item: %s\n", item1.name);
    item1.method(item1.args);
    // access item2
    printf("Accessing item: %s\n", item2.name);
    item2.method(item2.args);
    
    return(0);
}

// Boilerplate methods definitions
void hello(char *args[]) {
    char name[MAX_STRLEN] = "Bob";
    if (!args[0]) {
        strcpy(name, args[0]);
    }
    printf("Hello %s!\n", name);
}

void extend(char *args[]) {
    hello(args);
    printf("Welcome %s.\n", args[1]);
}

gcc response with -E flag precompilation:

<source>:43:91: warning: excess elements in scalar initializer
   43 |     struct mcl_item item1 = new_mitem("extend", &extend, (int)(sizeof((char *){"Charlie", "Delta"})/sizeof(char *)), (char *){"Charlie", "Delta"});
      |                                                                                           ^~~~~~~
<source>:43:91: note: (near initialization for '(anonymous)')
<source>:43:138: warning: excess elements in scalar initializer
   43 |     struct mcl_item item1 = new_mitem("extend", &extend, (int)(sizeof((char *){"Charlie", "Delta"})/sizeof(char *)), (char *){"Charlie", "Delta"});
      |                                                                                                                                          ^~~~~~~
<source>:43:138: note: (near initialization for '(anonymous)')
4
  • 1
    Please post an minimal reproducible example. Commented Feb 6, 2021 at 8:57
  • Yes, sorry. I think it is all you need now. Commented Feb 6, 2021 at 9:03
  • simply use -E option and see the preprocessed code godbolt.org/z/Mcnnvz . Then start to change your macros acordingly Commented Feb 6, 2021 at 9:47
  • Still, I can not see what is wrong with this initialization, why is "Delta" wrong: struct mcl_item item1 = new_mitem("extend", &extend, (int)(sizeof((char *){"Charlie", "Delta"})/sizeof(char *)), (char *){"Charlie", "Delta"}); Commented Feb 6, 2021 at 10:28

1 Answer 1

1

As it is used, (char *){__VA_ARGS__} is replaced with (char *){"Charlie", "Delta"}. This says to create a char * and initialize it with "Charlie" and "Delta". However, a char * is one thing (a pointer to char), and "Charlie" and "Delta" are two things. Hence the compiler warns you that there are too many initializers.

You can use (char *[]){__VA_ARGS__} to create an array of pointers to char *. When used as the operand of sizeof (or as the operand of unary &), this will give the size of the array, so it will work in the ARGSN macro. When used otherwise, the array will be automatically converted to a pointer to its first element, so it can be used to initialize the args member or other char ** objects.

However, your new_mitem function does not take a char ** parameter but rather declares char *arg, .... You will either want to pass it __VA_ARGS__ or change it to char **arg (with no ...) and pass it (char *[]){__VA_ARGS__}.

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

4 Comments

I pass now the __VA_ARGS__ directly as the last argument to the new_mitem() method; and I have removed the char *arg argument, as I found out, reading man 3 stdarg, that the type of the last argument does not matter. But still what I get from the last argument is just one string "Charlie". Like "Delta" makes no effect in the initialization.
@DanielFreeman: How to process variable arguments is a separate question.
Thank you for directing me. Can you help me figure out what I did wrong with variable processing or should I start a new thread for that? I post my current code here.
Ok.. I just drop all the va_arg business and go with simple pointer, much easier and less annoying.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.