News for hackers. Stuff that matters.
Re:Object-oriented Programming with ANSI-C (1993) [pdf] by lelanthran on Thursday June 22, 2023 @03:20AM (#36428989)
I'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.

Re:Object-oriented Programming with ANSI-C (1993) [pdf] by gpderetta on Thursday June 22, 2023 @06:09AM (#36429982)
This 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.

Re:Object-oriented Programming with ANSI-C (1993) [pdf] by Tomis02 on Thursday June 22, 2023 @07:01AM (#36430323)
To be honest I don't understand your comment, I think you're talking about something else, so I'll try to rephrase.

My point is that C is not an OOPL. There's two aspects of OOP: 1) the concrete language features 2) a pseudo-philosophical wishy-washy hippie territory where nothing is clear and everything is subjective, where you view all problems as something that is to be modelled with objects, and your own mental model is firmly centred around objects.

Since C lacks concrete OOP features, I will proceed to show why the latter aspect (philosophical OOP) is problematic.

In OOP, objects are entities composed of data and functions that operate on that data. That's what C++ classes usually are: members and methods bound syntactically together. OOP doesn't allow a method to belong to two objects, by definition and by C++ syntax.

Now, let's say you have a C API that offers 2 structs, A and B, and some functions F1A(A, ...), F1B(B, ...), and so on. The people who claim C is OOP language will say "a-ha! A is an object. The data (encapsulated too!) is in type A, and the functions F1A, F2A, ... are the methods. You could write a C++ class 'class A { private: data; public: F1A(...);...};'. Same for B. See? A and B are objects!"

This is the philosophical OOP in action, where "it looks like OOP" becomes "is OOP".

So what happens if the API adds a new function, FAB(A, B, ...), that operates on the internals of A and B? If we switch back to OOP parlance, if you had two classes, A and B, and wanted to add FAB to one of them, which class would FAB belong to?

Well, it's problematic because we're dealing with imaginary objects, so the answer can be whatever you want it to be.

You can arbitrarily say "pick the first parameter type to be the object that owns FAB". Well, what if it's FAB(const char*, A, B)? And even if A is the first one, doesn't FAB changing B break the all-precious object encapsulation? Once you introduce FAB, are these objects at all?

The problem of choosing the "owner" is a common one in C++ OOP because the objects are real (there's syntax to mark something as an object), whereas in C the objects are imaginary and, therefore, a fuzzy ambiguous concept. If you take the set of all functions whose 1st parameter is an int, does that form an object? If everything is an object, is anything an object?

But, like I said, this tends to be a non-problem in C (if you're not a OOP person) because you'll recognise that FAB(A,B) does something useful, and all this conceptual objects design is just noise that does not matter and you're happy to say "my code is not OOP but it does something useful in the best possible way".

Which is why I think that philosophical OOP (object thinking) is dubious, and the only way you can call a language OOP is if it has OOP features. Otherwise, if the only thing you're doing is applying your OOP mindset, I don't think it's fair to say that the language itself is OOP.