4

I'm trying to define a macro to generate a structure on my global scope like the code above:

#define BUFFER(size) \
struct { \
    unsigned short size = ##size; \
    unsigned short readIndex = 0; \
    unsigned short writeIndex = 0; \
    unsigned char dataPtr[##size##]; \
}

BUFFER(10) buffer10bytes;
BUFFER(50) buffer50bytes;

The problem is that apparently gcc is not evaluating this macro. Is it possible to archive? How?

Here is my compiler error:

In file included from ../usart.c:12:0:
../usart.c:14:8: error: expected identifier or '(' before numeric constant
 BUFFER(10) buffer10bytes;
        ^
../buff.h:24:17: note: in definition of macro 'BUFFER'
  unsigned short size = ##size; \
                 ^
../buff.h:24:22: error: pasting "=" and "10" does not give a valid preprocessing token
  unsigned short size = ##size; \
                      ^
../usart.c:14:1: note: in expansion of macro 'BUFFER'
 BUFFER(10) buffer10bytes;
 ^
../buff.h:25:27: error: expected ':', ',', ';', '}' or '__attribute__' before '=' token
  unsigned short readIndex = 0; \
                           ^
../usart.c:14:1: note: in expansion of macro 'BUFFER'
 BUFFER(10) buffer10bytes;
 ^
../buff.h:27:23: error: pasting "[" and "10" does not give a valid preprocessing token
  unsigned char dataPtr[##size##]; \
                       ^
../usart.c:14:1: note: in expansion of macro 'BUFFER'
 BUFFER(10) buffer10bytes;
 ^
../usart.c:14:8: error: pasting "10" and "]" does not give a valid preprocessing token
 BUFFER(10) buffer10bytes;
        ^
../buff.h:27:26: note: in definition of macro 'BUFFER'
  unsigned char dataPtr[##size##]; \
                          ^
../usart.c:15:8: error: expected identifier or '(' before numeric constant
 BUFFER(50) buffer50bytes;
        ^
../buff.h:24:17: note: in definition of macro 'BUFFER'
  unsigned short size = ##size; \
                 ^
../buff.h:24:22: error: pasting "=" and "50" does not give a valid preprocessing token
  unsigned short size = ##size; \
                      ^
../usart.c:15:1: note: in expansion of macro 'BUFFER'
 BUFFER(50) buffer50bytes;
 ^
../buff.h:25:27: error: expected ':', ',', ';', '}' or '__attribute__' before '=' token
  unsigned short readIndex = 0; \
                           ^
../usart.c:15:1: note: in expansion of macro 'BUFFER'
 BUFFER(50) buffer50bytes;
 ^
../buff.h:27:23: error: pasting "[" and "50" does not give a valid preprocessing token
  unsigned char dataPtr[##size##]; \
                       ^
../usart.c:15:1: note: in expansion of macro 'BUFFER'
 BUFFER(50) buffer50bytes;
 ^
../usart.c:15:8: error: pasting "50" and "]" does not give a valid preprocessing token
 BUFFER(50) buffer50bytes;
        ^
../buff.h:27:26: note: in definition of macro 'BUFFER'
  unsigned char dataPtr[##size##]; \
                          ^
make: ** [usart.o] Erro 1
ylima
  • 410
  • 5
  • 17
  • 1
    I don't think you want any of the `##` token pasting marks - you're not generating new tokens. Try renaming the arg to something else so it doesn't clash with the `size =` and omitting the `##`s? – Rup Apr 12 '15 at 23:04

1 Answers1

8

Take out the ##. That is for creating a new token from two tokens; but you are not doing that here, you do actually just want two tokens.

Also you cannot use the same name for a variable as you do for the macro parameter.

Another issue is that you can't put initializers inside the struct definition. So you'll have to modify the form of your macro, e.g.:

#define DECLARE_BUFFER(size_, name) \
struct { \
    unsigned short readIndex;
    unsigned short writeIndex;
    unsigned char dataPtr[size_]; \
} name = { 0 }

BUFFER(10, buffer10bytes);
BUFFER(50, buffer50bytes);

I have removed size as it seemed redundant: you can always go sizeof X.dataPtr to get that value. (note: Ptr is a poor name for an array). But you could include size if needed (as suggested by Remy in comments) if it's meant to change to represent the contents of the buffer or something:

#define DECLARE_BUFFER(size_, name) \
struct { \
    unsigned short size;
    unsigned short readIndex;
    unsigned short writeIndex;
    unsigned char dataPtr[size_]; \
} name = { size_, 0 }
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
M.M
  • 138,810
  • 21
  • 208
  • 365
  • 2
    Also, you can't have member initialisations within a C structure definition. – kaylum Apr 12 '15 at 23:10
  • 1
    What about the `size` field shown in the original question? `struct { unsigned short size; ...} name = { size_, 0 };` – Remy Lebeau Apr 12 '15 at 23:21
  • @RemyLebeau it seems redundant, updated my answer though – M.M Apr 12 '15 at 23:29
  • 1
    @MattMcNabb: It depends on how the buffers are being used. For instance, maybe they are passed by pointer to a function/library that accepts different buffer sizes so it has to read the `size` field to know the actual size of the `dataPtr` field. – Remy Lebeau Apr 12 '15 at 23:31
  • 1
    @RemyLebeau this struct is unnamed type so it could not be used as a function parameter. (Well I guess there could be some hack involving `void *` but it's hard to recommend that) – M.M Apr 12 '15 at 23:34
  • 1
    @MattMcNabb: They can be passed if the parameter is `void*`, or is even another fixed struct type that acts as a header which the buffers are type-casted to when passed. Again, depends on how these buffers are actually being used. – Remy Lebeau Apr 12 '15 at 23:35
  • 1
    @RemyLebeau The point is that size is redundant. It is always available via sizeof(dataPtr). That has already been mentioned in the answer. – kaylum Apr 13 '15 at 00:30
  • 1
    @AlanAu he means that someone might write `void func(void *bufs) { unsigned short size = *(unsigned short *)bufs;` etc. - non-portable but i've seen worse – M.M Apr 13 '15 at 00:31
  • 1
    Really? I guess so. But they still need to know to skip past readIndex and writeIndex to get to the actual data buffer. In any case, I don't think that was the spirit of the question and still think your original recommendation is more inline with that. – kaylum Apr 13 '15 at 00:34
  • Well, I was actually going to cast this struct to a pointer to a struct that is almost the same but has a pointer instead of a array on the last member so I can handle my operations to this buffer. Thank you guys a lot. I adapted my code using this new macro and it seems to work so far. – ylima Apr 13 '15 at 00:50
  • 1
    @ylima that cast won't work, pointers and arrays are different – M.M Apr 13 '15 at 00:56
  • @MattMcNabb you're right. But if dataPtr wich now may be renamed to just data or something like that is an array without size defined it seems to work just fine. Thank you. – ylima Apr 13 '15 at 01:12
  • 1
    @ylima then you are using the *flexible array member* feature of C, which actually does not need this macro at all! Just a single struct definition used by everyone. – M.M Apr 13 '15 at 01:15
  • @MattMcNabb I did some research but coudn't find a way to initialize a *flexible array member* without using malloc. Since it's a embeeded application I'd like to avoid this approach. So I think that for now the best option is to use this macro. – ylima Apr 13 '15 at 03:31
  • 1
    You don't necessarily need to use flexible arrays, just a 1-element array is enough, and then you can type-cast the starting address of the array to whatever type the array actually holds, as long as there is allocated memory to back it up. There are plenty of examples of the Win32 API using the very technique, where a generic struct with a 1-element array is passed around but actually has a larger payload at the array's address. Such as `TOKEN_PRIVILEGES`. So there needs to be a field to specify how many elements are actually in the array. – Remy Lebeau Apr 13 '15 at 05:42
  • 1
    @RemyLebeau accessing beyond the end of an array causes undefined behaviour ; the flexible array member is designed to avoid that – M.M Apr 13 '15 at 05:56
  • Well, I think this discussion is going a bit off topic. My original question has already been responded even though I still need some help to make things work. I think I should make a new question so it's gonna be easier for people searching for the same answer to find it. What do you guys think? – ylima Apr 13 '15 at 18:42
  • By the way, I think the flexible array option would be a lot cleaner but I just can't find a way to construct it without dynamically allocate it. – ylima Apr 13 '15 at 18:44