0

I stumbled on the following stuff in someone's source code in C:

typedef struct {
   u32 reg;
} reg_t;

#define _REG(r) ((const reg_t){.reg=(r)})

#define REG_A _REG(123)
#define REG_B _REG(456)
...

and it makes me wonder: what might be the purpose of such sorcery? Couldn't just a simple numerical constant be defined instead? Why do they define a structure, with just one member, and then use C99's compound literals to initialize it? Is there some obscure benefit that I'm missing here that requires such syntax?

(At first I was puzzled by that syntax as well, until I found out that it's a C99 extension. I knew about the "designated initializers", but I didn't know that they can be used this way, with something that looks like a type cast, but from what I understood, it's just C99's way of doing something akin to C++'s constructors. Still, I don't know why do they use this trick here in particular, and what would happen if they didn't.)

4
  • Some corrections: (type){values} is a compound literal, and .member=value is the designated initializer. And they are not just C99 extensions; they are integral part of C99 and above.
    – user694733
    Commented Jan 31 at 9:14
  • It is impossible to know for sure what the purpose of these are without knowing where REG_A or REG_B are used. But it looks like this is an attempt to increase type safety when dealing with some registers, which are presumably of type reg_t.
    – user694733
    Commented Jan 31 at 9:19
  • Did you ask the author? Whos source code is it? Where is the rest of the code available? From where and how did you obtain the code?
    – KamilCuk
    Commented Feb 3 at 7:46
  • If I could ask the author, I would, and there wouldn't be a need to ask here. It's some old proprietary Linux driver. I quoted just the relevant part, and modified it a bit (without losing its meaning of course, just the names). The constants are used as arguments to functions which are of type reg_t or const reg_t. So there is indeed a possibility that they used this trick to avoid implicit conversions from regular numeric constants and for better type safety and readability. As for corrections: in order to correct, there must be something that needs it. I see no such thing in my post. Commented Feb 5 at 13:43

1 Answer 1

0

what might be the purpose of such sorcery?

The purpose is to make _REG have type const reg_t.

Couldn't just a simple numerical constant be defined instead?

Anything is possible. Most probably, you could rewrite the code.

Why do they define a structure, with just one member, and then use C99's compound literals to initialize it?

To make the type of _REG be const reg_t. For type checking.

it's a C99 extension

An "extension" in C language, is an extra part, outside the standard. Specific compilers have specific "extensions", which "extend" the basic language. Like GCC compiler has __attribute__() function attributes syntax, which other compilers do not.

Compound literals are not extra. They are internal, inside C99 standard. All C99 compilers have compound literals.

Simple type checking example:

uint8_t reg_get(const reg_t ret);
int main() {
   reg_get(REG_A); // OK
   reg_get(123); // error
}

Note: with the code shown, there is no reason for them to be #define. I would do static const reg_t REG_A = {123};. Unless they are used in #ifdef.

Note: identifiers starting with underscore and upper case letters are reserved in C99. Use code is not allowed to use them.

Note: prefer to use standard uint32_t from stdint.h instead of user defined types.

3
  • Thanks. I think you might be right. This indeed might have been used to prevent implicit conversions from regular numerical constants and for better type/const safety and readability. So I accept your answer. As for the underscores, I'm aware that they are reserved for implementation, not to be used in user application code. But in system programming or embedded programming, such rules are often ignored or bent, because there's no runtime implementation yet, you write your own. And when using a library or system APIs, you use whatever APIs they tell you to use. If it's u32, then it's u32. Commented Feb 5 at 13:52
  • And by saying "extensions", I meant that the C99 standard "extended" the previous standard definitions of the language by introducing these new features. I never suggested that they are somehow "optional" or "outside of the standard (C99 is a standard after all, isn't it?). So I can't see how this sort of nitpicking is even relevant (but quite characteristic to Stack Overflow, from what I see :q ) Commented Feb 5 at 13:57
  • Hi. There are people with different proficiency levels here on stackoverflow. Noting the term "extension" has a specific meaning in C community, about reserved identifiers, about standard fixed-width types, was about sharing the knowledge. When you indeed feel that you know, just feel free to ignore it.
    – KamilCuk
    Commented Feb 5 at 14:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.