Hacker News new | ask | show | jobs
by ryao 301 days ago
You are wrong about those invocations being invalid. Such patterns happen in filesystem code fairly often. The best example off the top of my head is:

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.

1 comments

I don't agree that your example pattern matches to the example I'm complaining about. vfs_rename() is using old_dir's vtable on old_dir. The vtable matches the object.
It is not a vtable. It is a structure of function pointers called struct inode_operations. It is reused for all inodes in that filesystem. If you get it from one callback, you can safely use it on another struct inode on the same filesystem without a problem because of that, because nobody uses this like a vtable to implement an inheritance hierarchy. There are even functions in struct inode_operations that don’t even require the inode structure to be passed, such as ->readlink, which is most unlike a vtable since static member functions are never in vtables:

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.

Again, this just isn't responsive to my comments, which discuss the article. The article's author is clearly talking about OOP. You've invented some alternative article and are arguing about that instead; I'm not interested.
You were not discussing the article in your previous reply. That said, I have repeated explained why both you and the author article are wrong to describe that structure of function pointers as a vtable by giving examples of it containing things that are not allowed in vtables (if say a C++ compiler did this to its vtables, it could cause the wrong function to be called when trying to call a static member function). You ignore that.
The term "vtable" is not exclusive to C++. Trait function dictionaries in Rust are called vtables and behave as described here, there's not necessarily any 'this' or 'self' object.
The point of the vtable is to allow dynamic dispatch based on the actual type of an object. When you have a function that does not need a this pointer, it no longer depends on the type of the object and putting it in there anyway could cause you to execute a variant depending on the type of the object, which seems like a buggy undesirable behavior.