1

I am trying to assign a value inside my x-macro, but I don't really understand why it is not working:

#include <stdio.h>

typedef struct
{
    int a;
    int b;
} struct_t;

#define MY_LIST \
    MY_ELEMENT(a) \
    MY_ELEMENT(b)

#define MY_ELEMENT(x) struct_t x; \ 
x.a=33;
MY_LIST 
#undef MY_ELEMENT

int main(void)
{
    fprintf(stdout, "a: %d\n", a.a);
    return 0;
}

When compiling this I get the following error:

test.c:14:2: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or   ‘__attribute__’ before ‘.’ token
 x.a=33;

Could somebody explain why I am getting this error and how to solve this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
slethoprod
  • 29
  • 1
  • 4
  • OT: regarding: `typedef struct ...;` 1) it is best to separate the definition of a struct from a typedef for that struct 2) always include a 'tag' name for the struct as most debuggers use the tag name to be able to display the individual fields inside the struct – user3629249 Jan 12 '19 at 08:20
  • @user3629249 why are you suggesting to always separate the definition from a typedef? – slethoprod Jan 12 '19 at 08:21
  • because of the maintenance problems that arise when the fields need to be changed, or the code is being re-flowed, or for readability, or for flexibility – user3629249 Jan 12 '19 at 08:23
  • 1
    @user3629249. Could you give an example of where the separation would materially improve any of the qualities you just listed? – Mad Physicist Jan 12 '19 at 08:30
  • well, this may be subjective: when re-factoring the code. What if you want to change the name of that struct, say in 60 files? what if each file had the struct written like the posted code, but each of 60 files used a different typedef name?This is a simple example of why code should be written with each code block of data definition being separate – user3629249 Jan 12 '19 at 08:36
  • @user3629249 I already typedeffed the struct and used a generic name, why would people feel the need to wrap another typedef around it? If you want more clarity, just use a suitable variable name when instantiating the struct. – slethoprod Jan 12 '19 at 08:46
  • What if there are several programmers writing code on a project? What is some of those programmers wrote their code before you made the mess of the struct definition? – user3629249 Jan 12 '19 at 08:48
  • @user3629249 if they wrote the same stuff as I did this means there is code redundancy/duplication due to for instance bad communication. Either we have been writing code at the same time and this not communicated well or there code was there before mine which means I shoudl have pulled their branch in case we are working on git for instance – slethoprod Jan 12 '19 at 09:06

3 Answers3

5

You need to look into the preprocessed form of your code in your source file slethoprod.c. With GCC, you can get it with gcc -C -E slethoprod.c > slethoprod.i then inspect (with an editor or a pager) that slethoprod.i file.

It contains stuff like:

struct_t a; a.a = 33; struct_t b; b.a = 33;

which is obviously not valid C code (since it has some assignment outside of any function, at file scope; remember that an initialization in a declaration is not an assignment).

You might want to have some definition (with initialization) such as

struct_t a = {33};

or even (for readability purposes) a struct initialization like

struct_t b = {.a=33};

and you could play fancy preprocessor tricks to get that.

Look into some C reference site and/or study the C11 standard n1570 to learn more about C. Read also the documentation of your compiler (e.g. GCC) and of your preprocessor (e.g. cpp).

BTW, I personally feel that naming a global with the same name a as some field in it is poor taste (even if it is legal, since field names and global variables have different namespaces). For readability purposes, I recommend avoiding that.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 1
    I also see that the `MY_ELEMENT` macro is defined after being used, where I thought that pe-processing was single pass. – Paul Ogilvie Jan 12 '19 at 08:17
  • Why can't you assign anything outside a function? When initializing a global variable like eg `static int my_globa_var = 44;` you are doing an assignement outside a function as well. – slethoprod Jan 12 '19 at 08:20
  • @PaulOgilvie: Are you sure? I don't observe that on my computer. – Basile Starynkevitch Jan 12 '19 at 08:21
  • 3
    Initializations are not assignments – Basile Starynkevitch Jan 12 '19 at 08:21
  • @BasileStarynkevitch, actually I have never tried it, believing it was single pass (possibly learned from old compilers and never un-learned). I will try it. – Paul Ogilvie Jan 12 '19 at 08:25
  • @BasileStarynkevitch: yes, no longer single-pass. Still a good habit I think. – Paul Ogilvie Jan 12 '19 at 08:27
  • 1
    @PaulOgilvie: you are confused about how the C preprocessor works. It is single-pass, but your mental model of it is wrong. – Basile Starynkevitch Jan 12 '19 at 08:36
  • @BasileStarynkevitch, Huh?? To my knowledge single-pass means it is required that an identifier is defined before it can be used. Multi-pass means a second pass will resolve use of identifies used before defined. In single-pass the source is processed only once. The current behavior is multi-pass. – Paul Ogilvie Jan 12 '19 at 10:00
  • 2
    @PaulOgilvie: When the preprocessor sees the definition of MY_LIST, it simply tokenizes and saves the replacement text. If it is never used, it doesn't matter what's in the replacement text. When MY_LIST is invoked, then the preprocessor replaces the macro invokcation with the remembered text with suitable replacements. It then rescans the replacement text to see what else should happen. There's no need for MY_ELEMENT to be a macro when MY_LIST is defined. There'd only be a problem if MY_ELEMENT wasn't defined at the point when MY_LIST is invoked (the problem would be invalid function call). – Jonathan Leffler Jan 12 '19 at 16:36
  • @JonathanLeffler, got it! The rescanning of the replaced text until nothing can be replaced anymore. Thank you very much! – Paul Ogilvie Jan 12 '19 at 17:55
3

Assigning values to structure fields outside of function scope isn't appropriate, so your original code doesn't work

#define MY_ELEMENT(x) struct_t x; \ 
x.a=33;
MY_LIST //<-- Inaproppriate
#undef MY_ELEMENT

If you want to use current macro, you should write like this:

#include <stdio.h>

typedef struct
{
    int a;
    int b;
} struct_t;

#define MY_LIST \
    MY_ELEMENT(a) \
    MY_ELEMENT(b)

#define MY_ELEMENT(x) struct_t x; \ 
x.a=33;


int main(void)
{
  MY_LIST;
  fprintf(stdout, "a: %d\n", a.a);
  return 0;
} 

Or you can change your macro this way: #define MY_ELEMENT(x) struct_t x = {33, 0};

or even better this: #define MY_ELEMENT(x) struct_t x = {.a = 33};

and leave the rest of your code as is.

So that way you will initialize your variable right in macro.

Denis Sablukov
  • 3,360
  • 2
  • 26
  • 31
-2

The error you are referring mainly happens when there is a ; missing somewhere in code.
In this case if you add \ after x.a=33; and then call MY_LIST it goes away.
But then you should call MY_LIST in function for a to be defined in main
here is a working version of your code

#include <stdio.h>

typedef struct
{
    int a;
    int b;
} struct_t;

#define MY_LIST \
    MY_ELEMENT(a) \
    MY_ELEMENT(b)

#define MY_ELEMENT(x) struct_t x; \
x.a=33; 


int main(void)
{
    MY_LIST; 
    fprintf(stdout, "a: %d\n", a.a);
    return 0;
}
hessam hedieh
  • 780
  • 5
  • 18