Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macros expansion using ## operator

Hi every one the problem is that i have in this macro

 #define ADD_COMP(s1,s2,type)({\
  int _x=0;\
  for(int i=0;i<n_addrs;i++){\
    if(memcmp(s1,&(s2->##type),6)!=0){\
      _x=-1;\
    }else{\
      break;\
    }\
  }\
  _x;\
})

s1 is a simple array and s2 is a structure with 4 vectors as members like this

typedef struct example{
 char[6] one,
 char[6] two,
 char[6] three
}example;

Now for own reason i need to create a function that compares the s1 array of size 6 bytes with only a member of example, so for this purpose i wrote ADD_CMP using ## operator to be more generic as possible So i defined:

#define one
#define two
#define three

and i used the function different times in this way hoping in the succes of macros expansion

ADD_COMP(some_array,example1,one)
ADD_COMP(some_array,example1,two)
ADD_COMP(some_array,example1,three)

but the compiler return as error:

error: pasting "->" and "one" does not give a valid preprocessing token
error: pasting "->" and "two" does not give a valid preprocessing token
error: pasting "->" and "three" does not give a valid preprocessing token

How can i fix it without write the same function for every structure member?

like image 434
P.Carlino Avatar asked Jan 30 '23 06:01

P.Carlino


2 Answers

As the error suggests, there never was need for ##, as it is used to paste two preprocessing tokens to form a single one.

#define VAR(name, num) name##num
int VAR(foo, 1);  // foo and 1 must be pasted together as foo1, instead of foo 1

The macro should compile with fixes to several syntax errors and missing declarations

 #include<stdlib.h>
 #include<string.h>

 int n_addrs = 6;

 #define ADD_COMP(s1,s2,type) {\
  int _x=0;\
  for(int i=0;i<n_addrs;i++){\
    if(memcmp(s1,&(s2->type),6)!=0){\
      _x=-1;\
    }else{\
      break;\
    }\
  }\
  _x;\
}

typedef struct example{
 char one[6];
 char two[6];
 char three[6];
}example;

void foo(void)
{
    example* example1 = malloc(sizeof(example));
    char some_array[6];
    ADD_COMP(some_array,example1,one)
    ADD_COMP(some_array,example1,two)
    ADD_COMP(some_array,example1,three)
}

Note a compound statement { ... } isn't an expression and can not be used as such. By adding extra parentheses around it, you are using gnu's extension and is not standard C.

You should instead, just write a function to do this instead. You will then be able to return _x and with modern optimizers, there should be negligible to non-existant overhead.

like image 157
Passer By Avatar answered Feb 03 '23 16:02

Passer By


The token pasting operator is designed for cases where you want to glue two different preprocessor tokens into a single token. For example, you might write something like

#define Glue(x) x my##x_

and then write

Glue(int);

to get this variable declaration:

int my_int;

Here, token-pasting combined the token “my_” with the token “int” to form the new token “my_int,” a new single token representing a name.

Once you paste two tokens together, the preprocessor doesn’t rescan them to figure out whether it’s a compound of several different individual tokens. It treats whatever is formed as a single token. For example, this code won’t compile:

#define BadAdd(x, y) x##+##y
int z = BadAdd(137, 42);

The issue here is that the token pasting forms a single preprocessing token 137+42. The preprocessor then tries to map this preprocessing token to a single logical token, but there’s no single token that this could correspond to. Normally, C or C++ would treat this as three separate tokens (137, +, and 42), but since you’ve forcibly glued them together the compiler has no idea what it’s looking at.

Contrast this with a more traditional Add macro, which for the purposes of exposition omits tons of important parentheses:

#define Add(x, y) x + y
int z = Add(137, 42);

Here, Add(137, 42) expands out to a sequence of three tokens (137, +, 42), which the compiler in a later phase can then interpret as an addition expression.

The macro you wrote above is like the BadAdd macro. By gluing the -> token together with a field name, you end up with a single unit like ->one that the compiler can’t meaningfully interpret as a single token. Just delete the ## here - just like going from BadAdd to Add, this will generate a sequence of tokens rather than a single token, which is what you want here.

like image 36
templatetypedef Avatar answered Feb 03 '23 15:02

templatetypedef