3 ms·
> One thing I like to do in C++ is, on an embedded system, wrap an object around a hardware resource. It means that no other code needs to know about the detail
by loup-vaillant 2y ago
> One thing I like to do in C++ is, on an embedded system, wrap an object around a hardware resource. It means that no other code needs to know about the details of talking to that hardware.
That, my friend, is modular programming. It is not specific to hardware, and not at all exclusive to OOP. In fact, C can do it just as easily as C++:
typedef struct {
// Private stuff, do not rely on it
int bar;
float baz;
} foo_ctx;
int foo_init (foo_ctx *ctx);
void foo_cleanup (foo_ctx *ctx);
int foo_frobnicate(foo_ctx *ctx, int frobnicator);
And if you don't like putting private data in the header file, we can have a pointer to implementation and hide it even better than idiomatic C++ (I don't like this much, because this requires allocations the above avoids):
struct foo_ctx; // forward declare
typedef struct foo_ctx foo_ctx;
foo_ctx *foo_new ();
void foo_delete (foo_ctx *ctx);
int foo_frobnicate(foo_ctx *ctx, int frobnicator);
Some would say I'm just emulating OOP in C. Except I'm not. There's no class in there, just a data structure with opaque data in it, and functions that operate on it.
---
I find it especially tiring when people use classes to do modular programming, call it "OOP", and assume it doesn't exist elsewhere, or was an OOP invention. OOP coopted modules, and put light syntax sugar to call it its own.
And I can already hear you scream that I'm "emulating OOP in C", though what I'm actually doing is emulating modular programming in C. And if we insisted on making it look like C++, there are indeed 2 missing ingredients.
The first one would be overloading on the type of the first argument. With that we would no longer care about name clashing, and could write the module like this:
typedef struct { int bar; float baz; } foo;
int init (foo *this);
void cleanup (foo *this);
int frobnicate(foo *this, int frobnicator);
The second missing ingredient would be syntax sugar so `f(x, y)` could be written `x.f(y)` instead. That way the calling code could really feel OOP, even though it's little more than a thin layer of syntax sugar.
I have even done it myself. Wrote a scripting language for a client who wanted OOP. Instead I gave them function overloading and universal call syntax, and they didn't notice a thing.
---
When you think about what OOP really is, there's not much. There's the idea of instantiation, where instead of using global variables you define a data structure and instantiate it under one local name. OOP made this popular, though I'm not sure it's not another cooptation. Any language that have user defined types can do that.
Then there's inheritance, which is mostly a mistake (in my experience inheritance is best avoided unless absolutely necessary — and it is almost never necessary).
And finally there's subtyping, which is nearly interchangeable with closures. It's a bit more powerful, but it is also a bit more cumbersome, and the added power is rarely needed in my experience.
---
Anyway, when one repeats the above often enough, there comes a time where one had enough, and just write a quick dismissal. When it comes don't to it the conclusion is fairly simple:
OOP ain't so hot.
- AnimalMuppet 2y agoI agree that you can come very close to C++ with C. (After all, the original C++ implementation was cfront, which was just a C preprocessor). But to me, the difference between C++ and what you're saying is this: If you say typedef struct { // Private stuff, do not rely on it int bar; float baz; } foo_ctx; The only thing defending bar and baz from my meddling is the comment. If you're in C++ and you make bar and baz private, then I can't modify (or even read) them without going through the interfaces you design. The larger the codebase, the larger the team, and the more average-or-below coworkers you have, the more that matters.
- loup-vaillant 2y agoYou're correct. Which is why in practice I often consider using a forward declaration and pointer to implementation instead. But you know what, even that can be gamed. I have gamed it once: I copied & pasted the definition of the struct, and accessed its member from a place that was supposed to only have access to an opaque pointer. It worked. (To be fair, this isn't the kind of stuff you do by mistake, unlike merely accessing a struct member.)