5 ms·
What do you mean? The fact that C can do this is an example of how it's not limited. A lot of other languages instead require you to allocate everything in the
by deadbeeves 3y ago
What do you mean? The fact that C can do this is an example of how it's not limited. A lot of other languages instead require you to allocate everything in the heap and there's no possibility of passing things by copy, or of not accessing things through anything other than a pointer. C at least is capable of allocating and accessing at least some things directly on the stack.
- adwn 3y ago> The fact that C can do this is an example of how it's not limited. No, C is limited because it's mutually exclusive: either encapsulation, or zero overhead by-value passing. Other languages, like C++ or Rust, allow both at the same time.
- rightbyte 3y agoYou can do dummy defines of structs with the same size as the real one if you want the struct on the stack and encapsulation.
- e4m2 3y agoI've done this before, works quite well actually, but isn't very popular for some reason. > You can do dummy defines of structs with the same size Don't forget alignment. The general pattern is: https://godbolt.org/z/6je9Yb3rf https://godbolt.org/z/6je9Yb3rf.
- sfpotter 3y agoMaybe I'm missing the idea, but I'm not sure how this idea is supposed to work without using something like alloca. If you have: struct foo; in foo.h and: struct foo { int a; double x; ... }; in foo.c, you won't have sizeof(struct foo) available from bar.c, so your construction won't work in bar.c. You could define a function: size_t sizeof_foo(void); which just returns sizeof(struct foo) from inside foo.c, but since this size is now only known at runtime, you'll need to resort to alloca or VLAs...
- gpderetta 3y agoYou define public_foo in the header with the public members and a appropriately sized byte array for the private members. In the .c file you define private_foo, same a public_foo except that the byte array is replaced with the actual members. You static assert that size and alignment match and cast at function boundaries. You hope not to have violated strict aliasing rules. This is not completely unlike type erasure with small buffer optimization done by some c++ classes like std::function.
- sfpotter 3y agoOK, I understand what you’re doing now. Thanks for clarifying. The big downside here is that you’re leaking the size of the details into your ABI which wouldn’t happen with a fully opaque type… I could see some uses for it but haven’t felt a strong enough need to reach for it before, although it has occurred to me.
- gpderetta 3y agoOf course there is no way around that. A partial mitigation is the same as done for network protocols: reserve some space for future extensions.
- vore 3y agoAt that point you might as well just name your fields like DONTTOUCHTHIS_foo if you’re having to keep the private definition with fields in sync with the opaque public definition (and making sure the alignment and sizing are always in sync with the private one…)
- deadbeeves 3y agoEh. Arguably in C++ you don't get proper encapsulation just with private members, because changing the layout for those members changes the ABI.
- adwn 3y agoDon't let Perfect be the enemy of Good. The quality of encapsulation you can achieve in C++ is miles ahead of that of C, even if it isn't all that could ever be.
- pjmlp 3y agoFor example, user defined types that behave like built-ins, while preserving invariants. Specially great in IoT instead of macros accessing directly IO ports.
- dasyatidprime 3y agoThere's a third vertex to the triangle here: C++ and Rust allow both at the same time by dropping separate compilation. More thoroughly so in Rust than in C++, but header-focused libraries move C++ further toward whole-program compilation compared to C (maybe you could call it “large-overlapping-chunks-of-program compilation”).
- jll29 3y agoVery valid point. I keep fond memories of Modula-2, which has DEFINITION modules and IMPLEMENTATION modules, such that you can compile the former and the latter seprately. In Modula-2, I can specify an API in its DEFINITION module, and after compiling it, client applications can use such an API without the implementation being ready yet, and still I can compile the client and check if it is free of syntax errors.