3 ms·
> towards "mutable state should be localized, tightly scoped, Ironically, the way to do actual encapsulation / localized-mutable-state is to just use stack var
by mrkeen 2y ago
> towards "mutable state should be localized, tightly scoped,
Ironically, the way to do actual encapsulation / localized-mutable-state is to just use stack variables - the C way.
Fields (what OOP added) are leakier and less localized than stack variables.
- kibwen 2y ago> just use stack variables - the C way The problem is that C doesn't offer any resistance to using globals rather than stack variables, which led to much of the mess that the OOP revolution of the 80s was supposed to address. For every C program that eschews globals, you have a Toyota Camry, whose control systems famously contained "10,000 global variables" https://news.ycombinator.com/item?id=9643204 https://news.ycombinator.com/item?id=9643204
- mrkeen 2y ago> The problem is that C doesn't offer any resistance to using globals rather than stack variables Nor is there any resistance to writing 'public static' on a field anywhere in Java land. If that's a problem, you have to throw out most languages.
- trealira 2y agoC still has fields in structures; you just have to pass a structure as the argument to a function. As long as you're okay with syntax like "self->field" or "self.field" (given a parameter called "self"), you can still kind of do OOP, just that no fields are private.
- mrkeen 2y ago> no fields are private. Who needs the private keyword when C has better encapsulation than Java? -- foo.h typedef struct Bleh Bleh_t; Bleh_t *construct(int i); int getFoo(Bleh_t *bleh); -- main.c #include "foo.h" void main() { Bleh_t *bleh = construct(3); // int i = bleh->foo; int i = getFoo(bleh); } The commented-out line above will not compile, because the internals of Bleh_t are not exposed.
- trealira 2y agoThat's true, and many libraries do this. However, it comes at the cost of an unnecessary heap allocation that wouldn't otherwise be necessary if not for this encapsulation. If you want to do this, but allocate on the stack, you have to do something like this: typedef struct Bleh Bleh_t; size_t get_bleh_size(); void init_bleh(Bleh_t *, int); int main(void) { const size_t bleh_size = get_bleh_size(); Bleh_t *const bleh = alloca(bleh_size); init_bleh(bleh, 3); const int i = getFoo(bleh); } If you rewrite "init_bleh" to return the pointer it receives as the first parameter, you could hide it behind a macro: #define make_stack_bleh(i) init_bleh(alloca(get_bleh_size()), (i)) Used like this: Bleh *const bleh = make_stack_bleh(x); One advantage of this approach is that you could change the layout of a Bleh without recompiling the other object files, as your compiled code makes no assumptions about the size or the layout of the struct (i.e. the field offsets into a struct aren't hardcoded into the machine code emitted).