Abhishek Sagar Abhishek Sagar - 2 months ago 12
C Question

Why This macros is not expanding correctly?

I am trying to initialize the array as follows :

static tha_field_info_t person_t_fields[] = {
HA_FIELD_INFO(person_t, name, CHAR)
};


Relevant Data structures:

typedef struct _tha_field_info_{
char fname[128];
DATA_TYPE_t dtype;
unsigned int size;
unsigned int offset;
} tha_field_info_t;

typedef struct _person{
char name[30];
unsigned int age;
} person_t;


Macros used

#define HA_FIELD_OFFSET(st, name) ((int)&((st *)0)->name)
#define HA_FIELD_SIZE(st, name) sizeof (((st *)0)->name)

#define HA_FIELD_INFO (st, fname, dtype) \
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}


Seeing Compilation errors with this macros.

tha.h:35:28: error: ‘fname’ undeclared (first use in this function)
tha.h:35:35: error: ‘dtype’ undeclared (first use in this function)
tha.h:36:2: error: expected ‘}’ before ‘{’ token
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}
tha.h:36:3: error: stray ‘#’ in program
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}


However, If i Hardcode like this, then its work fine.

{"name", CHAR, sizeof(((person_t *)0)->name), ((int)&((person_t *)0)->name)}


Basically, i want to store the array with field information of structure person_t.

Answer

There are two types of definition directives in C:

#define OBJECT_LIKE_MACRO     followed by a "replacement list" of preprocessor tokens
#define FUNCTION_LIKE_MACRO(with, arguments) followed by a replacement list

What distinguishes these two types of macros is the token that follows the identifier after #define: if it's an lparen, it is a function-like macro, and otherwise it's an object-like macro. What's an lparen? Draft N1570 points out in appendix A section 3:

(6.10) lparen:
  a ( character not immediately preceded by white-space

As far as I know, this is one of the few cases in C where spacing matters (aside from // comments, line splicing, and preprocessing directives). And it kind of makes sense. After all, how would the preprocessor distinguish between any function-like macro and an object-like macro that has a replacement list starting with a ( token? For instance, the following is an object-like macro, rather than a function-like macro with bad syntax:

#define NULL (void*)0

Let's now answer your question. The problem is in these two lines:

#define HA_FIELD_INFO (st, fname, dtype)   \
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}

Because the ( character after HA_FIELD_INFO is "preceded by white-space", this is not the function-like macro that you intended it to be. Just remove that space:

#define HA_FIELD_INFO(st, fname, dtype)   \
{#fname, dtype, HA_FIELD_SIZE(st, fname), HA_FIELD_OFFSET(st, fname)}