Earlier quoted context omitted.
I think the author is talking about this: object->ops->start(object) Where not only is it explicit, but you need to specify the object twice (once to resolve the Vtable, and a second time to pass the object to the stateless C method implementation).
Nothing a little macro magic couldn't fix.. #define CALL(object, function, ...) (object->ops->function(object, __VA_ARGS__))
Object-oriented design patterns in C and kernel development
101–110 of 224 posts
Re: Object-oriented design patterns in C and kernel development
#102Earlier quoted context omitted.
this in C++ is just a regular pointer, it has no special footguns, just the typical ones you have with pointers in general
that's not really true - unlike a regular pointer, `this` is not allowed to be null, thus removing `if(this == nullptr)` is always a valid optimization to do.
Re: Object-oriented design patterns in C and kernel development
#103Earlier quoted context omitted.
that's not really true - unlike a regular pointer, `this` is not allowed to be null, thus removing `if(this == nullptr)` is always a valid optimization to do.
It absolutely is allowed to be null: #include struct Foo { void bar() { std::cout bar(); } will print 0
Foo *p = nullptr;
p->bar();
That's undefined behavior. It's "allowed" in the sense of "yes it's possible to write, compile and run that code", but the language makes no guarantees about what the results will be. So maybe not the most useful definition of "allowed".Re: Object-oriented design patterns in C and kernel development
#104Earlier quoted context omitted.
I think the author is talking about this: object->ops->start(object) Where not only is it explicit, but you need to specify the object twice (once to resolve the Vtable, and a second time to pass the object to the stateless C method implementation).
Nothing a little macro magic couldn't fix.. #define CALL(object, function, ...) (object->ops->function(object, __VA_ARGS__))
Re: Object-oriented design patterns in C and kernel development
#105Earlier quoted context omitted.
I think both of these invocations are invalid. Using object1's vtable methods on object2, obviously, but in the latter case: the vtable method should just point at the superclass impl, if not overridden. And if overridden and the child impl needs to call the superclass, it can just do so without dispatching through some vtable.
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 thi…
Re: Object-oriented design patterns in C and kernel development
#106Earlier quoted context omitted.
OOP cannot be a formalization of what predated it because the predating patterns support things that OOP explicitly disallows, like instantiation with unimplemented functions. That is extremely useful when you want to implement an optional function, or mutually exclusive functions such that you pick which is optional. This should be the case in the Linux VFS with ->read() and ->read_iter(). Also, ASTs were formalized…
OOP does not disallow instantiation with unimplemented functions, it's just an artefact of implementation in some languages.
So that is to say, if the base class has a certain function which must be implemented and must provide certain behaviors, then the derived class must implement that function and provide all those behaviors.
The POSIX functions like read and write do not have a contract which says that all implementations of them must successfully transfer data. Being unimplemented (e.g returning -1 with errno EOPNOTSUPP or whatever) is allowed in the contract.
OOP just wants a derived thing to obey the contract of the abtraction it is inheriting, so if you want certain liberties, you have to push them into the contract.
Re: Object-oriented design patterns in C and kernel development
#107Re: Object-oriented design patterns in C and kernel development
#108> The article describes how the Linux kernel, despite being written in C, embraces object-oriented principles by using function pointers in structures to achieve polymorphism. This technique predates object oriented programming. It is called an abstract data type or data abstraction. A key difference between data abstraction and object oriented programming is that you can leave functions unimplemented in your abstrac…
In Smalltalk and Objective-C, you just check at runtime whether an object instance responds to a message. This is the original OOP way. It's sad that OOP was corrupted by the excessively class-centric C++ and Java design patterns.
Re: Object-oriented design patterns in C and kernel development
#109Earlier quoted context omitted.
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 thi…
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.
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.
Re: Object-oriented design patterns in C and kernel development
#110Earlier quoted context omitted.
OOP does not disallow instantiation with unimplemented functions, it's just an artefact of implementation in some languages.
OOP only disallows inheritance with unimplemented functions when it's a contract violation. So that is to say, if the base class has a certain function which must be implemented and must provide certain behaviors, then the derived class must implement that function and provide all those behaviors. The POSIX functions like read and write do not have a contract which says that all implementations of them must successfu…