9 ms·
As far as I can tell, the author says that "ANSI-C is a full-scale object-oriented language" in a slightly sarcastic tone, as there's nothing particular to OOP
by Tomis02 3y ago
As far as I can tell, the author says that "ANSI-C is a full-scale object-oriented language" in a slightly sarcastic tone, as there's nothing particular to OOP languages that you can't do in C. But I've heard many people say (and mean it) they do OOP in C.
I find it a bit dubious to claim you're doing OOP in C, considering that C does not have objects. And I find it twice as dubious because you can translate any FP/OOP code to procedural C; at this rate, you can start claiming that your x86 binary is OOP.
I often hear that "FILE is an object, it's exactly as if C++ had class FILE {...}, the class is just syntactic sugar, the result of the compilation is identical".
This is false, as it's not "just syntactic sugar". A C++ class binds a function to a specific struct, and the function can "belong" to no other struct. When doing OOP you have to choose - function F belongs to either object A or to object B. It can't belong to both. Which is why OOP has endlessly silly discussions about whether class Car should Turn() the class Wheel, or should the class Wheel Turn() class Car. Or should we refactor it into class SteeringWheel that will Turn() both Car and Wheel.
This is not a thing in C. Linus Torvalds can add a function F that operates on the internals of both FILE and DIR at the same time, for example, and suddenly it's less clear who "owns" F and which one is the object.
Which is why, personally, I don't think doing dynamic dispatch with void* in C can be qualified as OOP, it's just a pattern that's been there ever since the language was introduced. It's only after you add specific OOP features to the language that it devolves into an OOP language.
- ameminator 3y agoHowever, one CAN do "object-oriented" programming in C. There are ways of modeling what a piece of data is (a struct with its members). There are ways of modeling how a piece of data can behave (functions operating on those structs/pointers to structs). Perhaps this is lacking in features and expressiveness compared to OOP-first languages like C#, C++ and others, but object-oriented concepts can apply in C - look to the compiler to organize private, public functions and ownership.
- Tomis02 3y agoI agree that you can _imagine_ that FILE and DIR are objects, you can imagine they have behaviour, you can apply your OOP mental model to procedural code. But it all breaks down the moment you add a function that operates on the internals of both FILE and DIR, because this function doesn't belong to any one object. And if you don't notice it, you keep thinking FILE and DIR are objects, when in fact they're not (a method can't "belong" to two objects, at least as far as objects are perceived in the software world). In short, I can agree that it's possible to write C with a OOP mindset. But (let's call it) imaginary OOP may not be actual OOP (as when enforced by the C++ compiler).
- gpderetta 3y agoIn an OO model, you can implement exactly that by having FILE and DIR derive from a common base class, then you can have your function take such a base and either act on the common subset or do a type query and cast to the derived type as needed (this is usually frowned on, but not uncommon). But yes, of course shoving everything under OO is bad. A bit of OO should be a mean to an end, not a inflexible design principle.
- Tomis02 3y ago> you can implement exactly that by having FILE and DIR derive from a common base class Ah, but now you're changing the problem :) we are not in C++, we do not have base classes, we are in C and have just two so-called objects. They don't belong to us, they're exposed as part of an API. To recap, objects are entities that bind together data and functions. You can't have a single function belong to two objects, by definition. Some people say that FILE and DIR plus the associated functions are objects; even though there's no syntax for it, they are objects conceptually, it's supposedly the same thing. But if FILE and DIR are 2 conceptual/imaginary objects, to which of the objects does F(FILE, DIR) belong? And, given they're not real objects, the answer can be whatever you want it to be. You can say that in this scenario they stop being objects at all, or you can say FILE owns the function because it's first (but then about the encapsulation, why does F use the internals of both), or you can say conceptual objects can be conjoined like siamese twins, or whatever you feel like it. You can say the set of all functions that take an int is an object. Once you go down this road, you can say anything is OOP if you look at it in a certain light. I understand that people can/do apply the OOP mindset to anything, it's their choice. But I don't think it's fair to characterize C as OOP since it doesn't even have syntax for objects. Syntax is real, people's mental models are not. But that's just me.
- skissane 3y ago> This is not a thing in C. Linus Torvalds can add a function F that operates on the internals of both FILE and DIR at the same time, for example, and suddenly it's less clear who "owns" F and which one is the object. It isn’t a thing in a C++ either if one makes all the fields public or if one uses friend. Member accessibility is not essential to the idea of OOP, since there are OOP languages which don’t have it, or in which it is merely a convention, or something which can be overridden-and a person can use an OOP language which has it yet fail to use it. So, I don’t think “C lacks private members” is a good argument against C being “OOP”. Especially when C actually has the moral equivalent of “private” given APIs can be based on opaque pointers, so your compilation unit has no idea what the fields or layout of some structure controlled by another is.
- Tomis02 3y ago> It isn’t a thing in a C++ But it is a thing in OOP. That's the point. C++ is multiparadigm. All the OOP gurus will tell you that objects shouldn't expose their internal data, only methods. That's why the so-called encapsulation is constantly brought up. People telling you C is OOP will say "FILE is an object, the FILE struct + fopen/fclose/fwrite constitute an object, FILE is opaque, we can only interact with its internals through the public methods". "DIR is an object, ...". And so on. And the problem is that when the Linux C API exposes a function that takes both a FILE and a DIR, and operates on both their internals, your whole idea of OOP-ness shatters because a method can only belong to a single object, by definition.
- skissane 3y ago> And the problem is that when the Linux C API exposes a function that takes both a FILE and a DIR, and operates on both their internals, your whole idea of OOP-ness shatters because a method can only belong to a single object, by definition. You can do the same in Smalltalk though-create a utility class containing a method which operates on one instance of each class, and create accessors to expose all their internals. If the ability to do that means C isn’t an OO language, then Smalltalk isn’t an OO language either. And if Smalltalk isn’t an OO language, then OO languages don’t exist.
- G3rn0ti 3y agoOOP is essentially a software architecture model. You can use this architecture in any language with a bit of discipline even if it does not give you all syntactical safety guarantees. At some point even object oriented assembler was a thing (as assembly routines may very well receive an address pointing to an object).
- rramadass 3y ago> OOP is essentially a software architecture model. Finally! Somebody pointed this out. OOP is merely a different way of structuring "Procedural/Imperative" programming. In the early days of OOP hype, the greats like Edsger Dijkstra, Niklaus Wirth were rather critical of it and denied that it was something very "Original". It was correctly pointed out as merely a technique for structuring code for "programming in the large". Once you understand this you can design and use OOP (for a specific definition) in any language.
- kallistisoft 3y agoYou can absolutely translate object oriented C++ to vanilla C. As a young teenager I would routinely convert C++ from books I got from my parents/library to C, because I didn't have access to a C++ compiler. The process isn't that difficult.... Take the members of the C++ object and create a struct to hold the data then create a 'constructor' function for populating that data. Afterwards you just call the member functions of the original class, as simple C functions, and pass the struct as the first argument. This in fact is what the actually happens at the assembly level when using a C++ compiler! The idea of an 'object' is purely an abstraction!
- lelanthran 3y agoI'm not sure I understand the reasoning here. It seems to me that you have it the wrong way around, so maybe I'm missing something really obvious: > This is not a thing in C. Linus Torvalds can add a function F that operates on the internals of both FILE and DIR at the same time, for example, and suddenly it's less clear who "owns" F and which one is the object. In this respect C is strongly-typed and you will most definitely see compilation errors if `void F(FILE *fp)` is called as `DIR *dp; F(dp)`. If you want to make a function F that operates on both `FILE` and `DIR` types, you have to bypass the type checks explicitly! IOW, it is always clear to the reader of the code that F can take either a `FILE` or a `DIR`, or only a `FILE` or only a `DIR`. There is never any ambiguity to the reader. OTOH, with C++ ... > A C++ class binds a function to a specific struct, and the function can "belong" to no other struct. Unlike C above, this is never clear to the reader of the code though. For example, looping over an array of instances calling `.F()` on each instance: the reader cannot know which implementation of `F` is being invoked. It's literally impossible to tell just by looking at the loop. Sometimes it's impossible to tell even by examining all the code because the specific method that is bound to an instance may only be determined at runtime.
- gpderetta 3y agoThis is C++: for (auto&& x: collection) x.foo(); You say you have no idea what foo is. This is C: for (int i = 0; i < collection_size; i++) foo(collection[i]); Now you say that which foo is being called is very obvious... ... but [1]: #define foo(x) x.foo(&x) x.foo is a function pointer and someone has been implementing OO in C (far fetched? it is literally the title of the article). With C++ at least if I M-. on foo emacs will show me all possible targets (or directly take me to foo implementation if not polymorphic); With C, good luck teaching your IDE your custom flavor of OO [2]. So, no, C and C++ are more similar than you think. The biggest difference is the presence of overloading which, in the static dispatch case, makes resolving the actual function from the function name slightly harder in C++ without tool help. Overloading has little to do with OO, although it is a form of static polymorphism. In the dynamic case they are equivalent (well except for the fact that you have to build the vtables by hand in C and the syntax is a bit awkward. [1] yes, yes, arguments are not protected against multiple evaluation. Sue me. [2] with emacs of course this is actually possible, just merely hard.