3
votes

P.S.- I have taken int and int * for simplicity purpose, It can also be struct and struct *.

I am trying to implement a macro to copy data present in one variable to other independent of the variable datatype, In the below solution I am using '_Generic' compiler feature. program 1:

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

#define copyVar(var,newVar) _Generic((var),int:({memcpy(newVar,(void *)&var,sizeof(int));}),\
        int *:({memcpy(newVar,(void *)var,sizeof(int));}),default:newVar=var)
int main() {
   int data = 2;
   int *copy;copy = (int *)malloc(sizeof(int));
   copyVar(data,copy);
   printf("copied Data=%i",*copy);
}

Program 2:

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

    #define copyVar(var,newVar) _Generic((var),int:({memcpy(newVar,(void *)&var,sizeof(int));}),\
                int *:({memcpy(newVar,(void *)var,sizeof(int));}),default:newVar=var)
 int main() {
           int data = 2;
           int *copy;copy = (int *)malloc(sizeof(int));
           copyVar(&data,copy);
           printf("copied Data=%i",*copy);
}

Now problem is, 'program 1' get compiled successfully despite some warning. But while compiling program 2 gcc throwing error:

error: lvalue required as unary '&' operand #define copyVar(var,newVar) _Generic((var),int:({memcpy(newVar,(void *)&var,sizeof(int));}),

and I assume this is due to since _Generic int: selection get preprocessed with one more ampersand

(void *)&&var

why is gcc evaluates all selection?

3
This all seems very wrong. What are you trying to achieve? Why memcpy an int? Why the asymmetry?n. 1.8e9-where's-my-share m.
You are copying data into an uninitialized pointer. The casts to void* should not be needed.Lundin
Please try to use more lines and white space on copyVar macro. It's very hard to read.user694733
Assuming you have 2 pointers to structures (type struct S *a, *b), you can simply do *a = *b;.user694733
No, a = b would copy address (type struct S *) from b to a. By dereferencing pointer with * you get access to pointee. Thus *a = *b copies the type struct S, from address pointed by b to address pointed by a.user694733

3 Answers

4
votes

Your code has various problems: you copy data into an uninitialized pointed, you have superfluous void* casts, you treat _Generic as some sort of compound statement instead of an expression, and so on.

But to answer your question, your code doesn't work because the result of &something is not a lvalue. Since the & operator needs a lvalue, you cannot do & &something. (And you cannot do &&something either because that gets treated as the && operator by the "maximum munch rule".)

So your code doesn't work for the same reason as this code doesn't work:

int x;
int**p = & &x;

gcc tells you that &x is not a lvalue:

lvalue required as unary '&' operand


EDIT - clarification

This _Generic macro, like any macro, works like pre-processor text replacement. So when you have this code in the macro:

_Generic((var), ...
int: ... (void *)&var
int*: ... (void)var

It gets pre-processed as

_Generic((&data), ...
int: ... (void *)& &data
int*: ... (void)&data

And all paths of the _Generic expression are pre-processed. _Generic itself is not part of the pre-processor, but gets evaluated later on, like any expression containing operators. The whole expression is checked for syntactic correctness, even though only one part of the expression is evaluated and executed.

1
votes

The indented original use of _Generic is with function pointers as here

#define copyVar(var,newVar) \
     _Generic((var),        \
        int:    function1,  \
        int*:   function2,  \
        default:function3)(&(var), &(newVar))

Here the generic expression chooses the function and then this function is applied to whatever are the arguments.

You would have to write the three stub functions that correspond to the three different cases.

If you have them small and nice and as inline in your header file, the optimizer will usually ensure that this mechanism does not have a run time overhead.

0
votes

This can be solved with a two level _Generic

#define copyVar(var,newVar) \
    _Generic((var), \
    int  :   ({  __auto_type _v = var; memcpy(newVar, (void *) _Generic((_v), int: &_v , int *: _v)  , sizeof(int));}) , \
    int *:   ({  __auto_type _v = var; memcpy(newVar, (void *) _v                                    , sizeof(int));}) , \
    default: newVar=var \
)