1

I'm looking for something like this snippet. I expect it to know, at compile time, wether it is dealing with an array or not, and avoid the following errors.

#include <stdio.h>


#define IS_ARRAY(x,type) _Generic((&x),          \
                                 type (*)[]: 1,  \
                                 default:   0)

#define GENERIC_ASSIGN(arg,type) if(IS_ARRAY(arg,type)){arg[0] = 1; arg[1] = 2;}else{arg = 2;}

int main(void)
{

    int foo = 0;
    int bar[10] = {0};

    GENERIC_ASSIGN(bar,int); //-->  error: assignment to expression with array type
    GENERIC_ASSIGN(foo,int); //--> error: subscripted value is neither array nor pointer nor vector  "arg[0] = 1; arg[1] = 2;"

    return 0;
}

When I do write GENERIC_ASSIGN(bar,int) i do know that 'bar' is an ARRAY, so does the compiler.

See this topic that explains one part of the problem here

The problem would have been solved easily if '#if' were allowed inside macros

Guillaume D
  • 2,202
  • 2
  • 10
  • 37
  • Maybe [this article](http://jhnet.co.uk/articles/cpp_magic) helps? It shows how to create IF_ELSE macros. – rveerd Mar 12 '20 at 16:02
  • nope, it's not helping ```IF_ELSE(IS_ARRAY(bar,int))(printf("is_array"))(printf("is_not_array"));``` >> ```error: expected expression before ‘int’;``` IS_ARRAY is expanded after IF_ELSE, so it expands to _IF_IS_ARRAY, but I need _IF_1 or _IF_0.... But interresting article anyway – Guillaume D Mar 12 '20 at 16:28
  • 1
    Note: I'd use `()` around `x` : `_Generic((&(x)), type (*)[]: 1, default: 0)` – chux - Reinstate Monica Mar 12 '20 at 18:54
  • Detail: "Assign an array ..." in not possible in C. Code can _initialize_ an array, but not _assign_ it even though `int bar[10] = {0};` looks like an assignment. Assignment `bar = {0};` or `bar = (char [10]){0};` not possible. So, Guillaume D, is your goal to assign an array (somehow) or initialize it or something else? – chux - Reinstate Monica Mar 12 '20 at 18:58
  • @chux-ReinstateMonica Not many expressions result in an lvalue so it's not really that important, unless you plan assignment expressions to the macro... which isn't possible in case of arrays. – Lundin Mar 12 '20 at 19:54
  • @chux-ReinstateMonica I want to change the value for one, or more of its fields : (pseudo-code)```arg[0] = 1; arg[1] = 2;``` or ``` for(i=0;i – Guillaume D Mar 13 '20 at 08:28

1 Answers1

2

You can't assign to arrays, so you will have to use memcpy. For example, have the macro create a compound literal of all initializers and then memcpy that one.

#include <stdio.h>
#include <string.h>

#define IS_ARRAY(x,type) _Generic((&x),                             \
                                 type (*)[]: 1,                     \
                                 default:    0)

#define INIT(arg, type, ...) memcpy(&(arg),                         \
                                    (type[]){__VA_ARGS__},          \
                                    sizeof((type[]){__VA_ARGS__})) 

#define GENERIC_ASSIGN(arg,type) IS_ARRAY(arg,type) ?               \
                                 INIT(arg,type,1,2) :               \
                                 INIT(arg,type,2)

int main(void)
{
  int foo = 0;
  int bar[10] = {0};

  GENERIC_ASSIGN(bar,int);
  GENERIC_ASSIGN(foo,int);

  printf("%d %d\n",bar[0], bar[1]);
  printf("%d\n",foo);

  return 0;
}

Notably, with this method it doesn't really matter what type you use, array or not. The size of the initializer list is all that matters.

gcc -O2 resolves this into a couple of register loads (x86):

    mov     edx, 2
    mov     esi, 1
    xor     eax, eax
    mov     edi, OFFSET FLAT:.LC0
    call    printf
    mov     esi, 2
    mov     edi, OFFSET FLAT:.LC1
    xor     eax, eax
    call    printf
Lundin
  • 195,001
  • 40
  • 254
  • 396