0

I was going through a header file of a micro-controller, when i found this,

 #define DEFA(name, address) __no_init union \
{ \
  struct \
  { \
    volatile unsigned char  name##_L; \
    volatile unsigned char  name##_H; \
  }; \
  struct \
  { \
    volatile unsigned short name##L; \
    volatile unsigned short name##H; \
  }; \
  __ACCESS_20BIT_REG__ name; \
} @ address;

I have multiple questions here,

  1. I didn't know that we can use union and structures within #define statements. How are they interpreted by the compiler?
  2. What does "name##_L" and "name##L" mean.?, especially "##".
  3. What is "__ACCESS_20BIT_REG__ name"?
  4. What is this new "@" symbol, I googled to find out about this symbol, and i find nothing related to the @ symbol anywhere, some people say that this is not C standard, is this true?, and what does it mean?

Someone, please explain me this peace of code, everything depends on this piece of the code, and everything either directly or indirectly uses this #define. :(

dhiru
  • 1
  • 2
  • "##" is the token concatenation feature of the C preprocessor: https://en.wikipedia.org/wiki/C_preprocessor#Token_concatenation – Simon F Feb 06 '19 at 09:26
  • 1
    *I didn't know that we can use union and structures within #define statements. How are they interpreted by the compiler?* – you can use whatever text you want in a praeprocessor symbol definition. All the preaprocessor does is (quite) stupid text replacement. – Swordfish Feb 06 '19 at 09:36
  • @Swordfish: Macro substitution replaces preprocessor tokens, not text, except for the # and ## operators. – Eric Postpischil Feb 06 '19 at 10:33
  • @EricPostpischil All preprocessor tokens i've seen so far were text ^^ – Swordfish Feb 06 '19 at 10:36
  • @Swordfish: If macro replacement substituted text, then `X-4` in a macro could become the floating-point literal `3e-4`. It cannot. Token replacement cannot form new tokens. The initial lexical analysis has already be performed and is not repeated. – Eric Postpischil Feb 06 '19 at 10:40

3 Answers3

2
  1. You can put most things inside macros, they are mostly just text replacement (though on a token by token level). Putting typedefs and variable declarations inside macros is however bad practice and should be avoided unless there's no other option.

  2. It pastes the token past and creates a new pre-processor token. In this case it creates member variable names. If you use DEFA(foo,0) you will get members named foo_H and foo_L.

    Notably, this particular macro would probably have been better if the fields were just named H and L. Why would anyone want to type reg.foo_H rather than foo.H? Using _L versus L postfix to distinguish between 8 and 16 bit access is not a very bright idea either.

  3. Some custom type used by the same code. Why they have a union between something called "20 bit" and 32 bit structs, I have no idea.

  4. A non-standard extension. Usually it is used for allocation of a variable at a specific address, which is the case here. Probably you have a memory-mapped hardware register and this macro is supposed to be a generic declaration for such registers.

Also notable, the union syntax isn't standard C. You need to name the union. The anonymous structs are standard C since C11, but I doubt this is from a modern compiler.

Overall, very bad code. A very typical kind of trash code you find in microcontroller register maps delivered by silicon/tool vendors. Not compliant with ISO C nor MISRA-C.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
1
  1. I didn't know that we can use union and structures within #define statements. How are they interpreted by the compiler?

Define statements are just macros. They are NOT interpreted by the compiler but by the preprocessor. That bit of code that is defined inside the macro is just copy-pasted everywhere, where you call/use that macro. If it contains valid C/C++ code, then it will compile in the next step also, once the preprocessor is done.

  1. What does "name##_L" and "name##L" mean.?, especially "##".

Yes, I was surprised by this ## once upon a time also. It is token concatenation. Basically, you can generate 'programmatically' variable/function names using it. So for example, you have,

#include <stdio.h> 
#define concat(a, b) a##b 
int main(void) 
{ 
    int foobar = 1; 
    printf("%d", concat(foo, bar)); 
    return 0; 
} 

would print 1, as it concatenates foo and bar together to form foobar.

  1. What is "__ACCESS_20BIT_REG__ name"?

__ACCESS_20BIT_REG__ seems to be an implementation-defined macro defined probably somewhere else in the code. The __ double underscore followed by a capital are reserved for implementation (so that they can be used for example by your uC manufacturer).

  1. What is this new "@" symbol, I googled to find out about this symbol, and i find nothing related to the @ symbol anywhere, some people say that this is not C standard, is this true?, and what does it mean?

This one has me stumped also. I don't think this is legal C.

EDIT:

Actually, regarding your point 4 i.e. the @ synmbol, I googled some more and found this stackoverflow question.

Duck Dodgers
  • 3,409
  • 8
  • 29
  • 43
0

I think the best way to explain this piece of code is by trying an example: So what results from the define DEFA(name, address)?

Using the define like e.g. DEFA(myName, myAddress) the preprocessor creates these lines of code:

__no_init union
{
  struct
  {
    volatile unsigned char  myName_L;
    volatile unsigned char  myName_H;
  };
  struct
  {
    volatile unsigned short myNameL;
    volatile unsigned short myNameH;
  };
  __ACCESS_20BIT_REG__ myName;
} @ myAddress;

So now to your questions:

  1. Interpreted as shown
  2. This is called token concatenation, is concates the given token to to an marco variableen.wikipedia.org/wiki/C_preprocessor#Token_concatenation
  3. ACCESS_20BIT_REG is probably a 20bit long data type marcro whick is defined somewhere else
Korni
  • 464
  • 2
  • 10
  • 1
    4 is quite relevant as this is a hardware register definition taken from a register memory map. Without it the macro would fill no purpose. – Lundin Feb 06 '19 at 09:44
  • So, this is what i infer, correct me if i am wrong... The whole UNION takes up 4 bytes, myName_L points to the 0-7 bits, myName_H points to the 8-15 bits, myNameL points to the 0-15 bits, myNameH points to the 16-31 bits, and the myName points to the 0-19 bits.., and the myAddress is the address of the 0th bit ? – dhiru Feb 06 '19 at 17:29