#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;
}A macro is effectively preprocessing facilitated by the language. You could always preprosess externally if you wanted, and there's nothing stopping you from doing that in the "Powerful Language (TM)" either.
Now whether people use macros and preprocessing usefully is another question, but not one to which the answer is "abolish macros for more language features". When used correctly, macros ARE power.
But if -sadly- you must use C, metaprogramming using macros is not a terrible thing.
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.
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.You're right about "enum class", but anonymous classes and structs are perfectly valid in C++:
https://github.com/floooh/sokol-samples/blob/bfb30ea00b5948f...
(also note the 'inplace initialization' which follows the state struct definition using C99's designated initialization)
(I'm the author of the linked-to article.)
https://shafik.github.io/c++/undefined%20behavior/2019/05/11...
C++ namespaces are a way to avoid library A's symbol "cow" clashing with library B's symbol "cow" without everything being named library_a_cow and library_b_cow all over the place which is annoying. I agree C would be nicer with such a namespace feature.
However this technique is about what happens when you realise your structure members x and y should be inside a sub-structure position, and you want both:
d = calculate_distance(s.x, s.y); // Old code
and
d = calculate_distance(s.position.x, s.position.y); // New
... to work while you transition to this naming.
The story is different in C++, but in practice many compilers support it the same as in C. Especially for games, where VC++ (PC, Xbox) and Clang (PS4/PS5) are the most commonly used compilers, it also works as expected. The trick is to only use type punning for trivial structs that don't invoke complications like con/de-structors or operators. The GP's example of a Vec3 struct that puns float x,y,z with float[3] is a very common one in games.
You don't need eval(), you've got strcpy()!
I think that C++ is better than C, but C is not that bad, even for large projects.
Sure, and operating systems have been written in assmebly too. The question is whether it would be better than just sufficient if Linux were written in C++, today (ie C++17 or 20, not something old). Switching now probably wouldn't be feasible (even ignoring technical reasons, the kernel developer community is familiar with the C codebase and code standards and bought into it), but if Linux were started today, would it be a better choice?
Maybe the answer is still no and C would still be chosen, but the choice today is very different than it was when Linux was started. Of course, maybe Rust or something would be chosen today instead.
> C++ would probably be very easy
Not necessary, besides some small? problems due to the C++ allowing "more magic optimizations" then C they would switch to a sub-set of C++, and it might be so you would need to communicate to all contributors that a lot of C++ things are not allowed. And it might be easier to simple not use C++. I mean if it would be that easy the kernel likely would have switched.
I've had far more success hard-firewalling C++ into its own box where programmers can use whatever they can get running than trying to limit people to subsets.
The union is only as big as necessary to hold its largest data member. The other data members are allocated in the same bytes as part of that largest member. The details of that allocation are implementation-defined but all non-static data members will have the same address (since C++14). It's undefined behavior to read from the member of the union that wasn't most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union.
What 6.5.2.3 simplifies is the use of unions of the type:
struct A{int type; DataA a;}
struct B{int type; DataB b;}
union U{A a;B b};
U u;
switch(u.type)...
Its not what is beeing used here.
std::variant is designed to deprecate all legitimate uses of union