9 ms·
Anonymous nested structs are also quite useful for creating struct fields with explicit offsets: #include <stdio.h> #include <stdint.h> #defin
by 10000truths 5y ago
Anonymous nested structs are also quite useful for creating struct fields with explicit offsets:
#include <stdio.h>
#include <stdint.h>
#define YDUMMY(suffix, size) char dummy##suffix[size]
#define XDUMMY(suffix, size) YDUMMY(suffix, size)
#define PAD(size) XDUMMY(__COUNTER__, size)
struct ExplicitLayoutStruct {
union {
struct __attribute__((packed)) { PAD(3); uint32_t foo; };
struct __attribute__((packed)) { PAD(5); uint16_t bar; };
struct __attribute__((packed)) { PAD(13); uint64_t baz; };
};
};
int main(void) {
// offset foo = 3
// offset bar = 5
// offset baz = 13
printf("offset foo = %d\n", offsetof(struct ExplicitLayoutStruct, foo));
printf("offset bar = %d\n", offsetof(struct ExplicitLayoutStruct, bar));
printf("offset baz = %d\n", offsetof(struct ExplicitLayoutStruct, baz));
return 0;
}
- gumby 5y agoOne of the very few things from C that I miss in C++ is anonymous structs and enums. I really don’t understand why they are not allowed. That is, C style enums don’t have to have a name but “type safe” (enum class) ones do. One classic use is to name an otherwise boolean option in a function signature; there’s typically no need to otherwise name it. C++ incompatibly requires a name for all struct and class declarations, again a waste when you will only have a single object of a given type.
- cjaybo 5y ago> C++ incompatibly requires a name for all struct and class declarations You're right about "enum class", but anonymous classes and structs are perfectly valid in C++: https://godbolt.org/z/7MbcqhnoK https://godbolt.org/z/7MbcqhnoK
- dataflow 5y agoTry struct S { struct { int x; }; }; under -pedantic and you'll get warning: ISO C++ prohibits anonymous structs [-Wpedantic]
- midjji 5y agoPedantic is for the older C++ standard, its not pedantic for the latter e.g c++11, I think this changed.
- dataflow 5y agoWell that blows my mind, I never realized pedantic ignores the language setting. Is this the only case where it does that?
- junon 5y agoNo, pedantic is for disabling compiler extensions. You still need to explicitly specify a standard.
- midjji 5y agogcc pedantic ignores the language flag, and clang and intel state they mirror gcc. So pedantic would be not C++11 even if you added that.
- midjji 5y agoUse a enum in a namespace, or anonymous namespace
- gumby 5y agoThis is an example of the desired use case: static obj& some_call (obj& o, enum struct { abandon, save } disposition) { ... }; This is a common case (and should be more common) to avoid using an obscure boolean flag, which can lead to bugs. It shouldn't need a name. An anonymous namespace just means the name itself won't leak out; under C++ rules I need the name even to specify the enum tag, which is absurd.
- nybble41 5y agoEach instance of "enum struct { abandon, save }" would denote a different type, yes? How would you write a compatible definition to go with your prototype?
- gumby 5y agoI don’t care that they are different types; if anything that would be a feature. The point is to prevent the “mysterious bool arguments” class of error. The question is if ADL could infer the scope of the enum, as template instant is toon can now infer the right thing and don’t always need the <T> notation.
- nybble41 5y agoI agree that you would want them to be different types; otherwise you would just use an unscoped enum. But that implies that if you write… /* example.h */ void f(enum struct { x, y } arg); /* example.c */ void f(enum struct { x, y } arg) { /* do something with arg */ } …then you've just created a function with two different overloadings based on distinct anonymous types which just happen to be spelled the same way. Without a type name I don't see any way you could define a function whose prototype would be compatible with the forward declaration. You also have conflicting definitions of "x" and "y" with the same names and scope but different types. Perhaps with GNU extensions you could use typeof(x) for the argument and avoid the conflict, but that isn't standard C++.
- WalterBright 5y ago> I really don’t understand why they are not allowed. I don't, either. Such were in D from 2000 or so. I also don't understand why `class` in C++ sits in the tag name space. I wrote Bjarne in the 1980s asking him to remove it from the tag name space, as the tag name space is an abomination. He replied that there was too much water under that bridge. D doesn't have the tag name space, and in 20 years not a single person has asked for it. This did cause some trouble for me with ImportC to support things like: struct S { ... }; int S; but I found a way. Although such code is an abomination. I've only seen it in the wild in system .h files.
- Gibbon1 5y agoThe only explanation I saw was that C++ standards guys were horrified by the idea of unpredictable side effects as a result of initialization of a struct. I think C++ though is adding them. What I'd like in c is designated function parameters. // these the same bar(.a = 10, .b = 12); bar(.b = 12, .a = 10);
- saagarjha 5y agoI suspect that to many C++ programmers, most initializations of structs have unpredictable side effects because of how complex they are ;)
- wott 5y agoYou can somewhat fake it by replacing your functions parameters list with a single struct parameter. struct bar_arguments { int a, b; }; int bar(struct bar_arguments args) { return 2*args.a + args.b;} #define bar(...) bar((struct bar_arguments) {__VA_ARGS__}) // usage (will print 32 three times) printf("%d\n", bar(10, 12)); printf("%d\n", bar(.a = 10, .b = 12)); printf("%d\n", bar(.b = 12, .a = 10)); The main drawback is that all parameters are now optional: it will not complain if you forget to assign all parameters, it will silently set them to 0 :-/ printf("%d\n", bar(10)); printf("%d\n", bar(.a = 10)); printf("%d\n", bar(.b = 12)); will print 20, 20 and 12. You can change those "default values", but then calling the function with regular positional parameters is impaired :-/
- jcelerier 5y ago... ? That's definitely not true, both anonymous structs and enums work fine in c++. https://wandbox.org/permlink/ICaQJXCaVOt9mXdP https://wandbox.org/permlink/ICaQJXCaVOt9mXdP
- gumby 5y agoNo, they are forbidden by the standard (take a look at cppreference). Some compilers implement the C behavior as an extension, so tell your compiler to follow the standard strictly. I don’t use extensions, even convenient ones, as I have to be able to run my code on a variety of compilers. If you don’t have to do that, some extensions (like this one) are really handy.
- jcelerier 5y agoFrom the standard, the enum-name is marked as optional in the enum grammar: in [dcl.enum](https://eel.is/c++draft/dcl.enum#11 https://eel.is/c++draft/dcl.enum#11) ; it is also referenced e.g. in [dcl.dcl]: > An unnamed enumeration that does not have a typedef name for linkage purposes ([dcl.typedef]) and that has a first enumerator is denoted, for linkage purposes ([basic.link]), by its underlying type and its first enumerator; such an enumeration is said to have an enumerator as a name for linkage purposes. And for classes/structs, [class.pre](https://eel.is/c++draft/class.pre#def:class,unnamed https://eel.is/c++draft/class.pre#def:class,unnamed) has explicit wording: > A class-specifier whose class-head omits the class-head-name defines an unnamed class. So both are entirely fine (and likewise, unions are too). Note that my links are for the current draft, but I just checked and this was already the case as far back as C++11. So I wonder where this persistent myth seems to come from.
- gumby 5y agoThis is awesome. I referenced cppreference, but that is not authoritative. Unfortunately, in the final draft, [class.pre] grammar makes the name mandatory even though the language you quote remains in the first textual paragraph following the grammar specification! The part of enums you quoted was C-compatible enums; anonymous scoped enums are explicitly forbidden: "The optional enum-head-name shall not be omitted in the declaration of a scoped enumeration" (dcl.enum 2). Sigh. I will send in a clarification at least on the class/struct/union side. Ideally the grammar would be fixed rather than that paragraph. The draft I looked at is https://timsong-cpp.github.io/cppwp/n4868/ https://timsong-cpp.github.io/cppwp/n4868/ (2020-10-18, shortly after the standard was approved).
- nyanpasu64 5y agoDo foo and bar deliberately overlap?
- 10000truths 5y agoYes, I was looking to demonstrate the flexibility of the approach by including overlapping fields.
- midjji 5y agoIf you write to either, accessing the other, even on the overlap, is undefined behaviour
- 10000truths 5y agoType punning/aliasing with unions is well defined in gcc. Linus even has a humorous rant about it on the topic: https://www.yodaiken.com/2018/06/07/torvalds-on-aliasing/ https://www.yodaiken.com/2018/06/07/torvalds-on-aliasing/ Sure, it's compiler-specific, but I'm already using `__attribute__((packed))` anyways.
- midjji 5y agoAll undefined behaviour is well defined for each compiler, what it really means is implementation defined and subject to change without notice or documentation with every compiler version or host os what flags are enabled or a thousand other things. Why use an approach which strictly relies on specific versions of specific compilers, rather than a completely portable and standard compliant struct with char array and a few char pointers? Or if you want a convenient interface and aren't explicitly writing for the kernel, switch to a restricted subset of C++ and do it right?
- rocqua 5y agoGCC explicitly states znd documents that type punning through unions will work as expected. This doesn't work by accident. It works very explicitly by design of GCC. Different GCC versions aren't randomly going to change documented behavior. And when they accidentally do, they will consider it a bug.
- midjji 5y agoThere are two kinds of undefined behaviour being invoked in using this. Its a horrible idea and a horrible code smell, get rid of it if you ever see something like this.
- 10000truths 5y agoI don't see any undefined behavior here. As I mentioned below, gcc explicitly documents type punning via unions as being well defined. But yes, this is compiler specific and is not guaranteed to work elsewhere.
- midjji 5y agoThere is absolutely undefined behaviour there. Undefined behaviour is defined not as nasal daemons but as: The compiler implementer does not guarantee that this behaviour will be hardware, circumstance, compiler version, or os consistent, nor that we will warn if we change this. Packed is technically not a undefined behaviour, but it is certainly a trap. Especially because the compiler macros leads people to make defines which select packed by compiler automatically. Then the special case of didn't recognize compiler is just left empty, meaning compiles but no longer does what you think.
- fifjdynb 5y agoYou don't get to decide what UB means. It really does mean nasal demons are a possibility: all bets are off when you run that executable. Use of the term "undefined behaviour" to mean something else may be on the increase, unfortunately (https://mars.nasa.gov/technology/helicopter/status/298/what-were-learning-about-ingenuitys-flight-control-and-aerodynamic-performance/ https://mars.nasa.gov/technology/helicopter/status/298/what-...), but if we're talking about C, it's meaning is fixed.
- midjji 5y agoI didnt, the deterministic nature of computer programs guarantees that what I wrote is the actual outcome.
- WalterBright 5y agoAnytime macros are used for metaprogramming, it's time to reach for a more powerful language.
- 10000truths 5y agoMacros are useful, as long as they're used sparingly. I think that in this case, it's used well - the struct is still perfectly readable, and the sole purpose of it is to make it so that you don't have to manually name the dummy fields. But you could totally just write out dummy1, dummy2, dummy3 etc. yourself if you want to get rid of the macros.
- WalterBright 5y ago> Macros are useful, as long as they're used sparingly. Everybody says that. Everybody believes it. And everybody goes to town making a rat's nest with macros, just like that snarl of cables under my desk that resist all attempts to make it nice. Myself included. I've even written an article about clever C macros. Look, ma! I was so proud of myself. But then I got older. I started replacing the macros in my C code with regular code. It turns out they weren't that necessary at all. I liked the C code a lot better when it didn't have a single # in it other than #include.
- Spivak 5y agoDoesn’t work in a lot of cases unfortunately. If you’re writing a library designed to be consumed by other languages you’re stuck with writing C abi compatible code which can be written in other languages that can “extern” them but it puts limits on what’s possible in those libraries.
- WalterBright 5y agoOne thing I do is to make my own interface to the 3rd party library with the ugly interface. Then that interface file is only one that sees it. It also may be true that one can't replace every use of the preprocessor. That shouldn't stop replacing what one can.
- 5y ago
- oshiar53-0 5y agoIMO you can still be explicit about field offsets by writing the struct in a usual way, and using static assertions to ensure offsets match the intended layout.