Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

77%
+5 −0
Q&A Are there technical reasons to pick one struct coding style over the other?

Arguments in favour of "struct tag style": Less namespace clutter. C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style...

posted 10mo ago by Lundin‭  ·  edited 10mo ago by Lundin‭

Answer
#4: Post edited by user avatar Lundin‭ · 2024-02-21T15:53:27Z (10 months ago)
  • **Arguments in favour of "struct tag style":**
  • ---
  • - **Less namespace clutter.**
  • C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style only occupies a name in the tag name space.
  • Meaning that struct tags will not collide with everything else in the ordinary name space and so using that style means less naming collisions overall, if the structs are exposed to the whole program through a header file.
  • (They may of course still collide with other structs, unions or enums however.)
  • Similarly, self-referencing structs that use "typedef style" will occupy twice the amount of identifiers:
  • `typedef struct node { .... struct node* next; } node;`
  • This occupies the identifier `node` in both the tag and ordinary name spaces.
  • **Arguments in favour of "typedef style":**
  • ---
  • - **Type swapping/punning/serializing struct types with the help of unions.**
  • Suppose you have some struct
  • ```c
  • struct s
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • ```
  • and later on realize that it would be handy to access this struct in more ways than one. With the nice little feature of C11 anonymous structs, we can rewrite this declaration into a union while remaining backwards-compatible:
  • ```c
  • union s
  • {
  • struct
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • struct
  • {
  • uint8_t x_lo; // assuming little endian & no padding
  • uint8_t x_hi;
  • uint8_t y_lo;
  • uint8_t y_hi;
  • };
  • uint8_t raw_data [4];
  • };
  • ```
  • You'll see unions like this all the time for embedded system MCU register maps, data protocol definitions and similar.
  • Thanks to C11 anonymous structs we can rewrite the struct like this and not break any existing code already using it as `obj.x`.
  • _However_, if we used "struct tag" style, we would have to rewrite all the code using what was once a struct and instead type `union` all over the place.
  • Similarly, in case of the mentioned register maps, we may have all manner of hardware registers where some are declared as structs and some as union. It becomes an unholy mess for the user of the register map if they are to type out either `struct` or `union` depending on register. I've never seen a MCU vendor-provided register map using "struct tag style", likely for this very reason.
  • - **Type-generic and/or macro trick programming.**
  • Type-generic programmming in old school C typically uses void pointers in combination with some enum to keep track of what type the data is. There may also be various macro tricks where you want to pass along a type as a pre-processor token, for whatever reason.
  • In both of these cases, it is highly inconvenient to have types consisting of multiple words with white space in between them.
  • Example - suppose we have a "X macro" listing all supported types by some program:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • ```
  • We might now use this for example to generate an enum, to get a unique integer value corresponding to each type supported:
  • ```c
  • typedef enum
  • {
  • #define ENUM_NAME(type) SOMETHING_##type,
  • SUPPORTED_TYPES(ENUM_NAME)
  • } type_t;
  • ```
  • So far so good. Then I want to add a struct to the list:
  • ```c
  • struct my_struct { int x; };
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • X(my_struct) \
  • ```
  • And bam: syntax error upon token concatenation `SOMETHING_##type`, because we suddenly try to create an enumeration constant called `SOMETHING_struct my_struct`.
  • Whereas `typedef struct { int x; } my_struct;` works seamlessly.
  • Similarly, only using one single pre-processor token for the type might help with macro "stringification" using the `#` operator.
  • - **Opaque types.**
  • Yes, that subjective Linux kernel document did manage to find a single non-subjective argument and ironically it was one _against_ using "struct tag style".
  • When declaring opaque types - see [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) - the preferred forward declaration in the header is `typedef struct my_struct my_struct;`. Mainly because the user of the opaque type need not and should not worry about what type they are actually dealing with.
  • But at the same time we cannot use `typedef` twice both for forward declaration and struct defintion. So the actual struct definition in the .c file would still have to be `struct my_struct {...};` even if the API of our opaque type always uses `my_struct*` without the `struct` keyword everywhere else. It kind of ends up a combination of the two styles.
  • - **C++ compatibility.**
  • In C++, structs are just a poor man's class and tags do not work quite the same - what you type after `struct` is the name of the type, period. You never type out `struct my_type x;` in C++. Or well, you can, but it looks quite alien and out of place among the rest of the language conventions.
  • **Arguments in favour of "struct tag style":**
  • ---
  • - **Less namespace clutter.**
  • C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style only occupies a name in the tag name space.
  • Meaning that struct tags will not collide with everything else in the ordinary name space and so using that style means less naming collisions overall, if the structs are exposed to the whole program through a header file.
  • (They may of course still collide with other structs, unions or enums however.)
  • Similarly, self-referencing structs that use "typedef style" will occupy twice the amount of identifiers:
  • `typedef struct node { .... struct node* next; } node;`
  • This occupies the identifier `node` in both the tag and ordinary name spaces.
  • **Arguments in favour of "typedef style":**
  • ---
  • - **Type swapping/punning/serializing struct types with the help of unions.**
  • Suppose you have some struct
  • ```c
  • struct s
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • ```
  • and later on realize that it would be handy to access this struct in more ways than one. With the nice little feature of C11 anonymous structs, we can rewrite this declaration into a union while remaining backwards-compatible:
  • ```c
  • union s
  • {
  • struct
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • struct
  • {
  • uint8_t x_lo; // assuming little endian & no padding
  • uint8_t x_hi;
  • uint8_t y_lo;
  • uint8_t y_hi;
  • };
  • uint8_t raw_data [4];
  • };
  • ```
  • You'll see unions like this all the time for embedded system MCU register maps, data protocol definitions and similar.
  • Thanks to C11 anonymous structs we can rewrite the struct like this and not break any existing code already using it as `obj.x`.
  • _However_, if we used "struct tag" style, we would have to rewrite all the code using what was once a struct and instead type `union` all over the place.
  • Similarly, in case of the mentioned register maps, we may have all manner of hardware registers where some are declared as structs and some as union. It becomes an unholy mess for the user of the register map if they are to type out either `struct` or `union` depending on register. I've never seen a MCU vendor-provided register map using "struct tag style", likely for this very reason.
  • - **Type-generic and/or macro trick programming.**
  • Type-generic programmming in old school C typically uses void pointers in combination with some enum to keep track of what type the data is. There may also be various macro tricks where you want to pass along a type as a pre-processor token, for whatever reason.
  • In both of these cases, it is highly inconvenient to have types consisting of multiple words with white space in between them.
  • Example - suppose we have a "X macro" listing all supported types by some program:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • ```
  • We might now use this for example to generate an enum, to get a unique integer value corresponding to each type supported:
  • ```c
  • typedef enum
  • {
  • #define ENUM_NAME(type) SOMETHING_##type,
  • SUPPORTED_TYPES(ENUM_NAME)
  • } type_t;
  • ```
  • So far so good. Then I want to add a struct to the list:
  • ```c
  • struct my_struct { int x; };
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • X(struct my_struct) \
  • ```
  • And bam: syntax error upon token concatenation `SOMETHING_##type`, because we suddenly try to create an enumeration constant called `SOMETHING_struct my_struct`.
  • Whereas `typedef struct { int x; } my_struct;` works seamlessly.
  • Similarly, only using one single pre-processor token for the type might help with macro "stringification" using the `#` operator.
  • - **Opaque types.**
  • Yes, that subjective Linux kernel document did manage to find a single non-subjective argument and ironically it was one _against_ using "struct tag style".
  • When declaring opaque types - see [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) - the preferred forward declaration in the header is `typedef struct my_struct my_struct;`. Mainly because the user of the opaque type need not and should not worry about what type they are actually dealing with.
  • But at the same time we cannot use `typedef` twice both for forward declaration and struct defintion. So the actual struct definition in the .c file would still have to be `struct my_struct {...};` even if the API of our opaque type always uses `my_struct*` without the `struct` keyword everywhere else. It kind of ends up a combination of the two styles.
  • - **C++ compatibility.**
  • In C++, structs are just a poor man's class and tags do not work quite the same - what you type after `struct` is the name of the type, period. You never type out `struct my_type x;` in C++. Or well, you can, but it looks quite alien and out of place among the rest of the language conventions.
#3: Post edited by user avatar Lundin‭ · 2024-02-21T15:51:34Z (10 months ago)
Typo
  • **Arguments in favour of "struct tag style":**
  • ---
  • - **Less namespace clutter.**
  • C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style only occupies a name in the tag name space.
  • Meaning that struct tags will not collide with everything else in the ordinary name space and so using that style means less naming collisions overall, if the structs are exposed to the whole program through a header file.
  • (They may of course still collide with other structs, unions or enums however.)
  • Similarly, self-referencing structs that use "typedef style" will occupy twice the amount of identifiers:
  • `typedef struct node { .... struct node* next; } node;`
  • This occupies the identifier `node` in both the tag and ordinary name spaces.
  • **Arguments in favour of "typedef style":**
  • ---
  • - **Type swapping/punning/serializing struct types with the help of unions.**
  • Suppose you have some struct
  • ```c
  • struct s
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • ```
  • and later on realize that it would be handy to access this struct in more ways than one. With the nice little feature of C11 anonymous structs, we can rewrite this declaration into a union while remaining backwards-compatible:
  • ```c
  • union s
  • {
  • struct
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • struct
  • {
  • uint8_t x_lo; // assuming little endian & no padding
  • uint8_t x_hi;
  • uint8_t y_lo;
  • uint8_t y_hi;
  • };
  • uint8_t raw_data [4];
  • };
  • ```
  • You'll see unions like this all the time for embedded system MCU register maps, data protocol definitions and similar.
  • Thanks to C11 anonymous unions we can rewrite the struct like this and not break any existing code already using it as `obj.x`.
  • _However_, if we used "struct tag" style, we would have to rewrite all the code using what was once a struct and instead type `union` all over the place.
  • Similarly, in case of the mentioned register maps, we may have all manner of hardware registers where some are declared as structs and some as union. It becomes an unholy mess for the user of the register map if they are to type out either `struct` or `union` depending on register. I've never seen a MCU vendor-provided register map using "struct tag style", likely for this very reason.
  • - **Type-generic and/or macro trick programming.**
  • Type-generic programmming in old school C typically uses void pointers in combination with some enum to keep track of what type the data is. There may also be various macro tricks where you want to pass along a type as a pre-processor token, for whatever reason.
  • In both of these cases, it is highly inconvenient to have types consisting of multiple words with white space in between them.
  • Example - suppose we have a "X macro" listing all supported types by some program:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • ```
  • We might now use this for example to generate an enum, to get a unique integer value corresponding to each type supported:
  • ```c
  • typedef enum
  • {
  • #define ENUM_NAME(type) SOMETHING_##type,
  • SUPPORTED_TYPES(ENUM_NAME)
  • } type_t;
  • ```
  • So far so good. Then I want to add a struct to the list:
  • ```c
  • struct my_struct { int x; };
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • X(my_struct) \
  • ```
  • And bam: syntax error upon token concatenation `SOMETHING_##type`, because we suddenly try to create an enumeration constant called `SOMETHING_struct my_struct`.
  • Whereas `typedef struct { int x; } my_struct;` works seamlessly.
  • Similarly, only using one single pre-processor token for the type might help with macro "stringification" using the `#` operator.
  • - **Opaque types.**
  • Yes, that subjective Linux kernel document did manage to find a single non-subjective argument and ironically it was one _against_ using "struct tag style".
  • When declaring opaque types - see [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) - the preferred forward declaration in the header is `typedef struct my_struct my_struct;`. Mainly because the user of the opaque type need not and should not worry about what type they are actually dealing with.
  • But at the same time we cannot use `typedef` twice both for forward declaration and struct defintion. So the actual struct definition in the .c file would still have to be `struct my_struct {...};` even if the API of our opaque type always uses `my_struct*` without the `struct` keyword everywhere else. It kind of ends up a combination of the two styles.
  • - **C++ compatibility.**
  • In C++, structs are just a poor man's class and tags do not work quite the same - what you type after `struct` is the name of the type, period. You never type out `struct my_type x;` in C++. Or well, you can, but it looks quite alien and out of place among the rest of the language conventions.
  • **Arguments in favour of "struct tag style":**
  • ---
  • - **Less namespace clutter.**
  • C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style only occupies a name in the tag name space.
  • Meaning that struct tags will not collide with everything else in the ordinary name space and so using that style means less naming collisions overall, if the structs are exposed to the whole program through a header file.
  • (They may of course still collide with other structs, unions or enums however.)
  • Similarly, self-referencing structs that use "typedef style" will occupy twice the amount of identifiers:
  • `typedef struct node { .... struct node* next; } node;`
  • This occupies the identifier `node` in both the tag and ordinary name spaces.
  • **Arguments in favour of "typedef style":**
  • ---
  • - **Type swapping/punning/serializing struct types with the help of unions.**
  • Suppose you have some struct
  • ```c
  • struct s
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • ```
  • and later on realize that it would be handy to access this struct in more ways than one. With the nice little feature of C11 anonymous structs, we can rewrite this declaration into a union while remaining backwards-compatible:
  • ```c
  • union s
  • {
  • struct
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • struct
  • {
  • uint8_t x_lo; // assuming little endian & no padding
  • uint8_t x_hi;
  • uint8_t y_lo;
  • uint8_t y_hi;
  • };
  • uint8_t raw_data [4];
  • };
  • ```
  • You'll see unions like this all the time for embedded system MCU register maps, data protocol definitions and similar.
  • Thanks to C11 anonymous structs we can rewrite the struct like this and not break any existing code already using it as `obj.x`.
  • _However_, if we used "struct tag" style, we would have to rewrite all the code using what was once a struct and instead type `union` all over the place.
  • Similarly, in case of the mentioned register maps, we may have all manner of hardware registers where some are declared as structs and some as union. It becomes an unholy mess for the user of the register map if they are to type out either `struct` or `union` depending on register. I've never seen a MCU vendor-provided register map using "struct tag style", likely for this very reason.
  • - **Type-generic and/or macro trick programming.**
  • Type-generic programmming in old school C typically uses void pointers in combination with some enum to keep track of what type the data is. There may also be various macro tricks where you want to pass along a type as a pre-processor token, for whatever reason.
  • In both of these cases, it is highly inconvenient to have types consisting of multiple words with white space in between them.
  • Example - suppose we have a "X macro" listing all supported types by some program:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • ```
  • We might now use this for example to generate an enum, to get a unique integer value corresponding to each type supported:
  • ```c
  • typedef enum
  • {
  • #define ENUM_NAME(type) SOMETHING_##type,
  • SUPPORTED_TYPES(ENUM_NAME)
  • } type_t;
  • ```
  • So far so good. Then I want to add a struct to the list:
  • ```c
  • struct my_struct { int x; };
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • X(my_struct) \
  • ```
  • And bam: syntax error upon token concatenation `SOMETHING_##type`, because we suddenly try to create an enumeration constant called `SOMETHING_struct my_struct`.
  • Whereas `typedef struct { int x; } my_struct;` works seamlessly.
  • Similarly, only using one single pre-processor token for the type might help with macro "stringification" using the `#` operator.
  • - **Opaque types.**
  • Yes, that subjective Linux kernel document did manage to find a single non-subjective argument and ironically it was one _against_ using "struct tag style".
  • When declaring opaque types - see [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) - the preferred forward declaration in the header is `typedef struct my_struct my_struct;`. Mainly because the user of the opaque type need not and should not worry about what type they are actually dealing with.
  • But at the same time we cannot use `typedef` twice both for forward declaration and struct defintion. So the actual struct definition in the .c file would still have to be `struct my_struct {...};` even if the API of our opaque type always uses `my_struct*` without the `struct` keyword everywhere else. It kind of ends up a combination of the two styles.
  • - **C++ compatibility.**
  • In C++, structs are just a poor man's class and tags do not work quite the same - what you type after `struct` is the name of the type, period. You never type out `struct my_type x;` in C++. Or well, you can, but it looks quite alien and out of place among the rest of the language conventions.
#2: Post edited by user avatar Lundin‭ · 2024-02-20T15:51:42Z (10 months ago)
  • **Arguments in favour of "struct tag style":**
  • ---
  • - **Less namespace clutter.**
  • C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style only occupies a name in the tag name space.
  • Meaning that struct tags will not collide with everything else in the ordinary name space and so using that style means less naming collisions overall, if the structs are exposed to the whole program through a header file.
  • (They may of course still collide with other structs, unions or enums however.)
  • Similarly, self-referencing structs that use "typedef style" will occupy twice the amount of identifiers:
  • `typedef struct node { .... struct node* next; } node;`
  • This occupies the identifier `node` in both the tag and ordinary name spaces.
  • **Arguments in favour of "typedef style":**
  • ---
  • - **Type swapping/punning/serializing struct types with the help of unions.**
  • Suppose you have some struct
  • ```c
  • struct s
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • ```
  • and later on realize that it would be handy to access this struct in more ways than one. With the nice little feature of C11 anonymous structs, we can rewrite this declaration into a union while remaining backwards-compatible:
  • ```c
  • union s
  • {
  • struct
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • struct
  • {
  • uint8_t x_lo; // assuming little endian & no padding
  • uint8_t x_hi;
  • uint8_t y_lo;
  • uint8_t y_hi;
  • };
  • uint8_t raw_data [4];
  • };
  • ```
  • You'll see unions like this all the time for embedded system MCU register maps, data protocol definitions and similar.
  • Thanks to C11 anonymous unions we can rewrite the struct like this and not break any existing code using it as `obj.x`.
  • _However_, if we used "struct tag" style, we would have to rewrite all the code using what was once a struct and instead type `union` all over the place.
  • Similarly, in case of the mentioned register maps, we may have all manner of hardware registers where some are declared as structs and some as union. It becomes an unholy mess for the user of the register map if they are to type out either `struct` or `union` depending on register. I've never seen a MCU vendor-provided register map using "struct tag style", likely for this very reason.
  • - **Type-generic and/or macro trick programming.**
  • Type-generic programmming in old school C typically uses void pointers in combination with some enum to keep track of what type the data is. There may also be various macro tricks where you want to pass along a type as a pre-processor token, for whatever reason.
  • In both of these cases, it is highly inconvenient to have types consisting of multiple words with white space in between them.
  • Example - suppose we have a "X macro" listing all supported types by some program:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • ```
  • We might now use this for example to generate an enum, to get a unique integer value corresponding to each type supported:
  • ```c
  • typedef enum
  • {
  • #define ENUM_NAME(type) SOMETHING_##type,
  • SUPPORTED_TYPES(ENUM_NAME)
  • } type_t;
  • ```
  • So far so good. Then I want to add a struct to the list:
  • ```c
  • struct my_struct { int x; };
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • X(my_struct) \
  • ```
  • And bam: syntax error upon token concatenation `SOMETHING_##type`, because we suddenly try to create an enumeration constant called `SOMETHING_struct my_struct`.
  • Whereas `typedef struct { int x; } my_struct;` works seamlessly.
  • Similarly, only using one single pre-processor token for the type might help with macro "stringification" using the `#` operator.
  • - **Opaque types.**
  • Yes, that subjective Linux kernel document did manage to find a single non-subjective argument and ironically it was one _against_ using "struct tag style".
  • When declaring opaque types - see [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) - the preferred forward declaration in the header is `typedef struct my_struct my_struct;`. Mainly because the user of the opaque type need not and should not worry about what type they are actually dealing with.
  • But at the same time we cannot use `typedef` twice both for forward declaration and struct defintion. So the actual struct definition in the .c file would still have to be `struct my_struct {...};` even if the API of our opaque type always uses `my_struct*` without the `struct` keyword everywhere else. It kind of ends up a combination of the two styles.
  • - **C++ compatibility.**
  • In C++, structs are just a poor man's class and tags do not work quite the same - what you type after `struct` is the name of the type, period. You never type out `struct my_type x;` in C++. Or well, you can, but it looks quite alien and out of place among the rest of the language conventions.
  • **Arguments in favour of "struct tag style":**
  • ---
  • - **Less namespace clutter.**
  • C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style only occupies a name in the tag name space.
  • Meaning that struct tags will not collide with everything else in the ordinary name space and so using that style means less naming collisions overall, if the structs are exposed to the whole program through a header file.
  • (They may of course still collide with other structs, unions or enums however.)
  • Similarly, self-referencing structs that use "typedef style" will occupy twice the amount of identifiers:
  • `typedef struct node { .... struct node* next; } node;`
  • This occupies the identifier `node` in both the tag and ordinary name spaces.
  • **Arguments in favour of "typedef style":**
  • ---
  • - **Type swapping/punning/serializing struct types with the help of unions.**
  • Suppose you have some struct
  • ```c
  • struct s
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • ```
  • and later on realize that it would be handy to access this struct in more ways than one. With the nice little feature of C11 anonymous structs, we can rewrite this declaration into a union while remaining backwards-compatible:
  • ```c
  • union s
  • {
  • struct
  • {
  • uint16_t x;
  • uint16_t y;
  • };
  • struct
  • {
  • uint8_t x_lo; // assuming little endian & no padding
  • uint8_t x_hi;
  • uint8_t y_lo;
  • uint8_t y_hi;
  • };
  • uint8_t raw_data [4];
  • };
  • ```
  • You'll see unions like this all the time for embedded system MCU register maps, data protocol definitions and similar.
  • Thanks to C11 anonymous unions we can rewrite the struct like this and not break any existing code already using it as `obj.x`.
  • _However_, if we used "struct tag" style, we would have to rewrite all the code using what was once a struct and instead type `union` all over the place.
  • Similarly, in case of the mentioned register maps, we may have all manner of hardware registers where some are declared as structs and some as union. It becomes an unholy mess for the user of the register map if they are to type out either `struct` or `union` depending on register. I've never seen a MCU vendor-provided register map using "struct tag style", likely for this very reason.
  • - **Type-generic and/or macro trick programming.**
  • Type-generic programmming in old school C typically uses void pointers in combination with some enum to keep track of what type the data is. There may also be various macro tricks where you want to pass along a type as a pre-processor token, for whatever reason.
  • In both of these cases, it is highly inconvenient to have types consisting of multiple words with white space in between them.
  • Example - suppose we have a "X macro" listing all supported types by some program:
  • ```c
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • ```
  • We might now use this for example to generate an enum, to get a unique integer value corresponding to each type supported:
  • ```c
  • typedef enum
  • {
  • #define ENUM_NAME(type) SOMETHING_##type,
  • SUPPORTED_TYPES(ENUM_NAME)
  • } type_t;
  • ```
  • So far so good. Then I want to add a struct to the list:
  • ```c
  • struct my_struct { int x; };
  • #define SUPPORTED_TYPES(X) \
  • X(int) \
  • X(double) \
  • X(my_struct) \
  • ```
  • And bam: syntax error upon token concatenation `SOMETHING_##type`, because we suddenly try to create an enumeration constant called `SOMETHING_struct my_struct`.
  • Whereas `typedef struct { int x; } my_struct;` works seamlessly.
  • Similarly, only using one single pre-processor token for the type might help with macro "stringification" using the `#` operator.
  • - **Opaque types.**
  • Yes, that subjective Linux kernel document did manage to find a single non-subjective argument and ironically it was one _against_ using "struct tag style".
  • When declaring opaque types - see [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) - the preferred forward declaration in the header is `typedef struct my_struct my_struct;`. Mainly because the user of the opaque type need not and should not worry about what type they are actually dealing with.
  • But at the same time we cannot use `typedef` twice both for forward declaration and struct defintion. So the actual struct definition in the .c file would still have to be `struct my_struct {...};` even if the API of our opaque type always uses `my_struct*` without the `struct` keyword everywhere else. It kind of ends up a combination of the two styles.
  • - **C++ compatibility.**
  • In C++, structs are just a poor man's class and tags do not work quite the same - what you type after `struct` is the name of the type, period. You never type out `struct my_type x;` in C++. Or well, you can, but it looks quite alien and out of place among the rest of the language conventions.
#1: Initial revision by user avatar Lundin‭ · 2024-02-20T15:51:01Z (10 months ago)
**Arguments in favour of "struct tag style":**
---

- **Less namespace clutter.**

  C has a peculiar name space system where all identifiers belong to one of: labels, tags, members, ordinary. Struct tag style only occupies a name in the tag name space.

  Meaning that struct tags will not collide with everything else in the ordinary name space and so using that style means less naming collisions overall, if the structs are exposed to the whole program through a header file.

  (They may of course still collide with other structs, unions or enums however.)

  Similarly, self-referencing structs that use "typedef style" will occupy twice the amount of identifiers:

  `typedef struct node { .... struct node* next; } node;`

  This occupies the identifier `node` in both the tag and ordinary name spaces.

  
**Arguments in favour of "typedef style":**
---

- **Type swapping/punning/serializing struct types with the help of unions.** 

  Suppose you have some struct

  ```c
  struct s 
  { 
    uint16_t x; 
    uint16_t y; 
  };
  ```

  and later on realize that it would be handy to access this struct in more ways than one. With the nice little feature of C11 anonymous structs, we can rewrite this declaration into a union while remaining backwards-compatible:

  ```c
  union s
  {
    struct
    { 
      uint16_t x; 
      uint16_t y; 
    };    
    struct
    {
      uint8_t x_lo; // assuming little endian & no padding
      uint8_t x_hi;
      uint8_t y_lo;
      uint8_t y_hi;
    };

    uint8_t raw_data [4];
  };
  ```

  You'll see unions like this all the time for embedded system MCU register maps, data protocol definitions and similar.

  Thanks to C11 anonymous unions we can rewrite the struct like this and not break any existing code using it as `obj.x`.

  _However_, if we used "struct tag" style, we would have to rewrite all the code using what was once a struct and instead type `union` all over the place.

  Similarly, in case of the mentioned register maps, we may have all manner of hardware registers where some are declared as structs and some as union. It becomes an unholy mess for the user of the register map if they are to type out either `struct` or `union` depending on register. I've never seen a MCU vendor-provided register map using "struct tag style", likely for this very reason.

- **Type-generic and/or macro trick programming.**

  Type-generic programmming in old school C typically uses void pointers in combination with some enum to keep track of what type the data is. There may also be various macro tricks where you want to pass along a type as a pre-processor token, for whatever reason.

  In both of these cases, it is highly inconvenient to have types consisting of multiple words with white space in between them.

  Example - suppose we have a "X macro" listing all supported types by some program:

  ```c 
  #define SUPPORTED_TYPES(X) \
    X(int)                   \
    X(double)                \
  ```

  We might now use this for example to generate an enum, to get a unique integer value corresponding to each type supported:

   ```c
   typedef enum
   {
     #define ENUM_NAME(type) SOMETHING_##type,
     SUPPORTED_TYPES(ENUM_NAME)
   } type_t;
   ```

  So far so good. Then I want to add a struct to the list:

   ```c
   struct my_struct { int x; };

   #define SUPPORTED_TYPES(X) \
     X(int)                   \
     X(double)                \
     X(my_struct)             \ 
   ```

   And bam: syntax error upon token concatenation `SOMETHING_##type`, because we suddenly try to create an enumeration constant called `SOMETHING_struct my_struct`.

  Whereas `typedef struct { int x; } my_struct;` works seamlessly.

  Similarly, only using one single pre-processor token for the type might help with macro "stringification" using the `#` operator.

- **Opaque types.**

  Yes, that subjective Linux kernel document did manage to find a single non-subjective argument and ironically it was one _against_ using "struct tag style". 

  When declaring opaque types - see [How to do private encapsulation in C?](https://software.codidact.com/posts/283888) - the preferred forward declaration in the header is `typedef struct my_struct my_struct;`. Mainly because the user of the opaque type need not and should not worry about what type they are actually dealing with.

  But at the same time we cannot use `typedef` twice both for forward declaration and struct defintion. So the actual struct definition in the .c file would still have to be `struct my_struct {...};` even if the API of our opaque type always uses `my_struct*` without the `struct` keyword everywhere else. It kind of ends up a combination of the two styles.

- **C++ compatibility.**

  In C++, structs are just a poor man's class and tags do not work quite the same - what you type after `struct` is the name of the type, period. You never type out `struct my_type x;` in C++. Or well, you can, but it looks quite alien and out of place among the rest of the language conventions.