1

I was trying to create and initialize a struct using a variable length macro. I want to be able create/initialize the struct with '0's and then initialize some fields of the struct with values that were optionally passed in.

Is this possible to do? I was trying the following:

#define INIT_ENTRY(x, ...) \
    entry_t x = {.id = 0, .age = 0} \
    x.id  = <I want to use arg1 here>
    x.age = <i want to use arg2 here if it exists> 

Is there a way to accomplish this?

KAM
  • 115
  • 1
  • 3
  • 5
  • Would something like the following use-case meet your need: `x = INIT_ENTRY(x, 1)` would do about `(entry_t){.id = 1}` and `x = INIT_ENTRY(x, 1, 2)` would do about `(entry_t){.id = 1, .age = 2}` ? What is the max length needed - arbitrary? BTW: `x.id = ` is not _initialization_, but assignment. – chux - Reinstate Monica Aug 18 '17 at 21:50

2 Answers2

1

It can be done with a combination of argument counting and concatenation.

  1. First you need a macro that counts it's arguments, which I'll call NARGS This was asked and answered here. Note that answers are compiler-dependent.

  2. Next you need a series of macros for all specific cases:

    #define INIT_ENTRY_0(x) entry_t x = {.id = 0, .age = 0}
    #define INIT_ENTRY_1(x, _id) entry_t x = {.id = _id, .age = 0}
    #define INIT_ENTRY_2(x, _id, _age) entry_t x = {.id = _id, .age = _age}

  3. You also need a concatenation macro:

    #define CONCAT(a, b) a##b

  4. Last, use concatenation to choose the correct one:

    #define INIT_ENTRY(x, ...) CONCAT(INIT_ENTRY_, NARGS(__VA_ARGS__))(x, #__VA_ARGS__)

ugoren
  • 16,023
  • 3
  • 35
  • 65
0

If you keep the same order of initializers in your macro as in your initialization list, then given you're always initializing everything else to 0, you can simply do this:

#define _INIT_ENTRY_UTILITY(x, ID_, AGE_, ...) \
   entry_t x = {.id = ID_, .age = AGE_}
#define INIT_ENTRY(...) _INIT_ENTRY_UTILITY(__VA_ARGS__, 0, 0, )

I assume your real use case has more than 2 parameters; if you take this approach, make sure to mimic its form exactly to maintain portability. In particular:

  • Changing to #define INIT_ENTRY(x, ...) for "clarity" will break calls that look like INIT_ENTRY(foo) on standard C preprocessors. What's worse is that it will actually work on some popular cpp's (such as gnu) that support such things as an extension, so the portability issue's hidden.
  • Removing the final comma in the call to _INIT_ENTRY_UTILITY from INIT_ENTRY's replacement list leads to the same portability issue
H Walters
  • 2,634
  • 1
  • 11
  • 13