object1->op->start(object2)
superclass->op->start(object) object1->op->start(object2)
superclass->op->start(object)In the first example, object1 and object2 can very much be of the same type or compatible types/subtypes/supertypes. Having vtables per object as opposed to per class to me indicates, that it IS intended to modify the behaviour of an object by changing it's vtable. Using the behaviour of another object of the same type to treat the second object, seams valid to me.
In the second case, it's not about the child implementation dispatching to the superclass, it's about some external code wanting to treat it as an object of the supertype. It's what in other languages needs an upcast. And the supertype might also have dynamic behaviour, otherwise you of course wouldn't use a vtable.
Upcasting is fine, but generally speaking the expected behavior of invoking a superclass method on an object that is actually a subclass is that the subclass method implementation is used (in C++, this would be a virtual/override type method, as opposed to a static method). Invoking a superclass-specific method impl on a subclass object is kind of weird.
In C you can also change the "class" of an instance as needed, without special syntax. Maybe you need to already call a method of the new/old class, before/after actually changing the class type.
> is that the subclass method implementation is used
The entire point of invoking the superclass method is, because the subclass has a different implementation and you want to use the superclass implementation.
Maybe it's a bit due to its evolution. It started with a language that should have all features every possible, that was to complicated to be implemented at the time. Then it was dumbed down to a really simple language. And then it evolved along side a project adding the features, that are truly useful.
error = old_dir->i_op->rename(rd->new_mnt_idmap, old_dir, old_dentry, new_dir, new_dentry, flags);
https://github.com/torvalds/linux/blob/master/fs/namei.c#L51...
That is a close match for the first example, with additional arguments.
It is helpful to remember that this is not object oriented programming and not try to shoehorn this into the paradigm of object oriented programming. This is data abstraction, which has similarities (and inspired OOP), but is subtly different. Data abstraction does not automatically imply any sort of inheritance. Thus you cannot treat things as necessarily having a subclass and superclass. If you must think of it in OOP terms, imagine that your superclass is an abstract class, with no implemented members, except you can instantiate a child class that is also abstract, and you will never do any inheritance on the so called child class.
Now, it is possible to implement things in such a way where they actually do have something that resembles a subclass and a superclass. This is often done in filesystem inode structures. The filesystem will have its own specialized inode structure where the generic VFS inode structure is the first member and thus you can cast safely from the generic inode structure to the specialized one. There is no need to cast in the other direction since you can access all of the generic inode structure’s members. This trick is useful when the VFS calls us via inode operations. We know that the inode pointer is really a pointer to our specialized inode structure, so we can safely cast to it to access the specialized fields. This is essentially `superclass->op->start(object)`, which was the second example.
Data abstraction is a really powerful technique and honestly, object oriented programming rarely does anything that makes me want it over data abstraction. The only thing that I have seen object oriented programming do better in practice than data abstraction is marketing. The second example is similar to C++’s curiously recurring template pattern, which adds boilerplate and fighting with the compiler with absurdly long error messages due to absurdly verbose types to achieve a result that often at best is the same thing. On top of those headaches, all of the language complexity makes the compile times slow. Only marketing could convince someone that the C++ OOP way is better.
https://www.kernel.org/doc/html/latest/filesystems/vfs.html
As I said previously, it is helpful to remember that this is not object oriented programming and not try to shoehorn this into the paradigm of object oriented programming. Calling this a vtable is wrong.
What speed limits can this road possibly have is a question I want to ask about this specific road. Yet this can be answered by referring to the country, which is already known when you create the road. But the user that asks this question can still ask this about roads in different countries, so this question still is valid.
Different objects can have different methods. When the method to be used is known at the time of object creation it can be chosen by assigning the appropriate function pointer in the vtable. The method itself might not necessarily need a instance pointer though.
When I invoke object->Foobar() I want to invoke the appropriate method for this object, from whatever class that might be. This is exactly what's happening in the kernel here.
When I actually intend to call the method from ClassA, I would either call something like object->base->Foobar() or ClassA->Foobar(object). Note how this is the very example that you are replying to: https://news.ycombinator.com/user?id=1718627440
Yes, you claimed it is like a static member function, I don't think it can be.
> The proper way to implement static functions is through static dispatch
Yes, but we are talking about dynamic dispatch here.
To quote your earlier comment:
> which is most unlike a vtable since static member functions are never in vtables
You conclude it isn't a vtable, I conclude, it's not a static member function, because it uses dynamic dispatch.
I don't think we actually disagree on how and when to use static and dynamic dispatch.
Omitting the this pointer also breaks inheritance, since hypothetical child classes would not be able to override the definition while using the this pointer. Having to edit the parent class to be able to do that is not how OOP works.
This is not OOP nor is it intended to be.
No it is not. Unless you can show me a virtual overridden static member function.
> since hypothetical child classes would not be able to override the definition while using the this pointer
Yes! That's the entire idea here. The non-hypothetical, but indeed existing, child classes are not able to access the object. This is to prescribe potential behaviour for child implementations.