0

I'm writing some macro that write a structure {a, b, c, d, e}. But this macro is able to receive 5 arguments or less. If it receives less than 5 arguments, it should put 0 in the structure.

I was trying to make a macro that repeat "0, " N times but as this N is the number of arguments, it is not a "hard-coded" value, so I cannot concatenate it with my macro that repeat the 0.

I was wondering if there is any way to have a default value on the macro themselves.

#define REPEAT1(s) s
#define REPEAT2(s) s, s
#define REPEAT3(s) s, REPEAT2(s)
...
#define REPEATN(n, s) REPEAT ## n (s)
...
{__VA_ARGS__, REPEATN((5 - VA_ARGS_NUM(__VA_ARGS__)), 0)} 
// can't work because ## will not create a valid token with REPEAT and (5 - ....)
Arthur
  • 181
  • 2
  • 10
  • 4
    C does this by default: any initialisation with fewer elements than expected will have the remaining ones filled in with `0`s. I.e: `struct foo bar = {5}` is equivalent to `struct foo bar = {5, 0, 0, 0, 0}` – Kninnug Jun 14 '17 at 20:47
  • 1
    @Kninnug: That is not quite correct for the general case. Integers are initialised to `0`. Other types are set to `0.0` (floats), resp. _null pointers_. None of the latter is necessarily identical to a value of all bits `0`. – too honest for this site Jun 14 '17 at 20:52
  • @Olaf I did not mean to imply they would be filled with 0 bits, but rather that the elements would be set to their equivalent of zero. IIRC the `0` literal is translated to `0.0` for `double`s and null pointers for such types – Kninnug Jun 14 '17 at 20:58
  • 1
    For the general problem of a macro that iterates: https://stackoverflow.com/a/25429401/315052 – jxh Jun 14 '17 at 20:58
  • 1
    @Kninnug: Ok. I just wnated to point that out for other beginners, as that's the main difference for `memset(…, 0)` (which is near useless imo for non integer arrays). – too honest for this site Jun 14 '17 at 21:07
  • Is the question "if some arguments are missing, all items should be set to zero" or is it "if some arguments are missing, missing arguments should be set to zero". Because I read the question as the former, but you accepted an answer proposing the latter. – Lundin Jun 15 '17 at 09:48
  • I posted an alternative answer in case you are actually looking for the solution mentioned in the question. – Lundin Jun 15 '17 at 11:33

2 Answers2

3

C sets to 0 by default all others which are not initialized.

For example:

typedef struct {
    int a,b,c,d,e,f;
} myStruct;

myStruct str = {1, 2, 3};

Now you have values str.a = 1, str.b = 2 and str.c = 3 while all others are set to 0.


However, if you do this:

myStruct str;

Then you don't set anything to 0, specially not if this is local variable declared on stack. If variable is global, then it can be set to 0 if linker puts code to set global variables to 0 on startup.

unalignedmemoryaccess
  • 7,246
  • 2
  • 25
  • 40
  • It causes a warning (missing-field-initializers), is the C99 standard specify the 0 initialization or it is the compiler behaviour ? – Arthur Jun 14 '17 at 21:06
  • The second half is not entirely accurate. Variables with static storage duration, such as those defined at file scope, or in a function with the `static` qualifier, are always initialized – Kninnug Jun 14 '17 at 21:08
  • @Kninnug variables with `static` keyword are not put on stack, and are not necessary set to `0` by default. This is the job of compiler/linker to add code to reset everything to `0` in RAM at startup. In general, all global and static variables will be set to `0` but this can be disabled. – unalignedmemoryaccess Jun 14 '17 at 21:09
  • @tilz0R The standard doesn't mention any stack, but does state how static objects are to be initialized: C11§5.1.2 [1]: *"All objects with static storage duration shall be initialized (set to their initial values) before program startup. The manner and timing of such initialization are otherwise unspecified."* And §6.7.9 [8]: *"If an object that has static or thread storage duration is not initialized explicitly, then" [list of rules for initializing to zero values]* – Kninnug Jun 14 '17 at 21:13
  • @Kninnug I do agree with you, but in some cases this can be prevented and I can give you example. Just create chat if you want to know more. – unalignedmemoryaccess Jun 14 '17 at 21:14
  • Ok it solves my problem then ! But what if I wanted to have them to 1 by default ? – Arthur Jun 14 '17 at 21:18
1

If it receives less than 5 arguments, it should put 0 in the structure.

This can be solved by using compound literals. First we can create a macro with compound literals to determine the number of VA_ARGS, since there is no standard macro for that:

#define ARGS_N(...) (sizeof((int[]){ __VA_ARGS__ }) / sizeof(int))

This can be used to determine the number of items. In this case if there are exactly 5, the struct should be initialized with those values, otherwise it should be initialized to 0.

We can then use the above macro to create a conditional initializer list. Given some struct:

typedef struct
{
  int a;
  int b;
  int c;
  int d;
  int e;
} test_t;

We could initialize instances of that struct through a custom method:

test_t t1 = TEST_INIT(1,2,3); 

Which would be implemented as:

#define TEST_INIT(...) (ARGS_N(__VA_ARGS__)==5) ? (test_t){__VA_ARGS__} : (test_t){0}

Unfortunately some compilers (gcc) will whine about missing initializers since both paths of the conditions are expanded, even if only one is evaluated. In this case we can safely tell gcc to shut up:

#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

Full program:

#include <stdio.h>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"

typedef struct
{
  int a;
  int b;
  int c;
  int d;
  int e;
} test_t;

#define ARGS_N(...) (sizeof((int[]){ __VA_ARGS__ }) / sizeof(int))

#define TEST_INIT(...) (ARGS_N(__VA_ARGS__)==5) ? (test_t){__VA_ARGS__} : (test_t){0}

int main (void)
{
  test_t t1 = TEST_INIT(1,2,3);  
  test_t t2 = TEST_INIT(1,2,3,4,5);  
  printf("%d %d %d %d %d\n", t1.a, t1.b, t1.c, t1.d, t1.e);
  printf("%d %d %d %d %d\n", t2.a, t2.b, t2.c, t2.d, t2.e);
}

#pragma GCC diagnostic pop

Output:

0 0 0 0 0
1 2 3 4 5
Lundin
  • 195,001
  • 40
  • 254
  • 396