From 59bdeb93accef510b127f35d43d9885c24b7193b Mon Sep 17 00:00:00 2001 From: liquidev Date: Fri, 28 Feb 2025 15:12:18 +0100 Subject: [PATCH] C++ without classes --- content/programming/cxx-without-classes.tree | 88 ++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 content/programming/cxx-without-classes.tree diff --git a/content/programming/cxx-without-classes.tree b/content/programming/cxx-without-classes.tree new file mode 100644 index 0000000..1cb84bc --- /dev/null +++ b/content/programming/cxx-without-classes.tree @@ -0,0 +1,88 @@ +%% title = "C++ without Classes" + +- one thing I often see in people's code in C++ is putting _everything_ into a class. + + - there's nothing wrong with putting code that actually, y'know, _uses the class_, in the class, but it irks me when I see things like helper functions and other implementation details being in the class. + + - private functions have the unfortunate consequence of being part of the public header, even though they are implementation details! + this means if you edit them, you have to edit both the .cpp implementation file, as well as the .h header. + +- which is precisely what C++ without Classes is about. +since updating method signatures is a pain in the ass... just don't do it! + + - declare all your fields public, and let `static` functions within your `.cpp` file work the magic. + + - and here you might say: "but I don't _want_ to have these fields public! it breaks encapsulation!" + to which I respond: so make your data structure private! + +- example of C++: + +#### world.h + +```cpp +struct World +{ + // ... fields ... + + void update(const Update_Context& context); + +private: + void update_aims(const Update_Context& context); +}; +``` + +#### world.cpp + +```cpp +void World::update(const Update_Context& context) +{ + update_aims(context); +} + +void World::update_aims(const Update_Context& context) +{ + // ... +} +``` + +- example of C++ without Classes: + +#### world.h + +```cpp +struct World +{ + // ... fields ... +}; + +void update(World& world, const Update_Context& context); +``` + +#### world.cpp + +```cpp +static void update_aims(World& world, const Update_Context& context) +{ + // ... +} + +void update(World& world, const Update_Context& context) +{ + update_aims(world, context); + // ... +} +``` + + - suddenly, the publicly available part of `world.h` has gotten smaller---there's one method less. + the implementation detail `update_aims` does not leak into the header; we don't have to update the signature in two places if we ever want to change it, and it does not cause a recompilation if we edit it. + + - I took the `update` method out of the struct to signal the lack of coupling. + _anyone_ can do what `update` does; it doesn't matter. + every function is at the same level as the struct itself. + + - in my own code, outside this contrived example, I also put functions related to a data structure in a namespace---in this case, `update(World&, const Update_Context&)` would be `world::update(World&, const Update_Context&)`. + this is purely a stylistic preference---I like the extra explicitness over leaning on overloads. + +- I've been enjoying this style a lot. +it takes edge cases like `this` out of the language, and results in simpler, more easily editable code. +so I wholeheartedly recommend giving it a try!