Live data from Hacker News

Object-oriented design patterns in C and kernel development

oshub.org

121–130 of 224 posts

Re: Object-oriented design patterns in C and kernel development

#121

Yup. I've often wonder why the aversion to C++ since they are obviously using objects. Is it that they don't want to also enable all the C++ language junk like templates or OO junk like inheritance?

Here's one example. For us, it's more a tradeoff rather than an aversion. There's pros (manual memory management in C) and cons (manual memory management in C) for each. We do math operations (dense and sparse matrix math for setting up and solving massive systems of differential equations) on massive graphs with up to billions of nodes and edges. We use C in parts of the engine because we need to manage memory at a…

What parts of it can't just be compiled as C++ code? (unless it has to do with the subtle difference in implicit lifetime object rules)

IMO it's much easier to write resleaks/double-frees with refcounted objects in C than it is in C++

Re: Object-oriented design patterns in C and kernel development

#122

Earlier 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

Just to cite what the others have told you: https://godbolt.org/z/bWfaYrqoY

    /app/example.cpp:10:6: runtime error: member call on null pointer of type 'Foo'
    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /app/example.cpp:10:6 
    /app/example.cpp:3:8: runtime error: member call on null pointer of type 'Foo *'
    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /app/example.cpp:3:8

Re: Object-oriented design patterns in C and kernel development

#123
post #109
post #105

Earlier quoted context omitted.

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 eve…

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.

Re: Object-oriented design patterns in C and kernel development

#124
post #13
post #9

Earlier quoted context omitted.

Compiler design does predate object oriented programming. The first compiler was made by John Backus et al at IBM in April 1957. As for abstract data types, they originated in Lisp, which also predates object oriented programming.

Actually, no. "AN ALGORITHMIC THEORY OF LANGUAGE", 1962 https://apps.dtic.mil/sti/tr/pdf/AD0296998.pdf In this paper they are known as plexes, eventually ML and CLU will show similar approaches as well. Only much latter would Lisps evolve from plain lists and cons cells.

You caused me to do some digging. That publication is dated November 1962. The Lisp 1.5 manual’s preface is dated August 17, 1962, which is even older. It describes lambdas and property lists, which seem like they can be used to implement ADTs, although I do not have a Lisp 1.5 interpreter since those are obsolete, so I cannot verify that. Computer history articles claim that Simula, the first object oriented language, was born in May 1962, but was not actually operational until January 1965:

https://history-computer.com/software/simula-guide/

Thus, while I had thought Lisp had ADT concepts before the first OOL existed, now I am not sure. My remark that they originated in Lisp had been said with the intention that I was talking about the first language to have it. The idea that the concept had been described outside of an actual language is tangential to what I had intended to say, which is news to me. Thanks for the link.

Re: Object-oriented design patterns in C and kernel development

#125

Note that this is using interfaces (i.e. vtables, records of function pointers), not full object-orientation. Other OO features, like classes and inheritance, have much more baggage, and are often not worth the associated pain.

vtables contain function pointers to functions that take “this” pointers. The author mentions struct file_operations as an example of a vtable. struct file_operations contains a pointer to a function that does not take “this” pointer. It is not even a vtable.

Re: Object-oriented design patterns in C and kernel development

#126
post #110

Earlier quoted context omitted.

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…

I would call returning something being implemented as a stub rather than being unimplemented. When something is unimplemented and you try to call it, you crash due to a NULL/invalid pointer dereference, not get an error back. Of course, as far as getting things done is concerned, the two might as well be the same, but as far as how the language works, the two are different.

That's just it; in the POSIX world, the library functions like read and write are just a facade. They call something in a kernel, and that something examines the file descriptor and dispatches a lower level function. It's possible that that is literally unimplemented: as in a null function pointer in some structure. The facade converts that to a -1 return with an approprriate `errno` like EOPNOTSUPP (operation not supported).

Re: Object-oriented design patterns in C and kernel development

#127
post #91

Earlier quoted context omitted.

Both languages let you use explicit `this` but don’t mandate it. The “static closure” approach is great. I don’t like having to explicitly pass `this` as a parameter to every method call as in the OP (or worse, the confusing Python approach of forcing `self` to be explicitly written in every non-static method signature but having it be implicitly passed during method calls). What I don’t like is being able to referen…

> int x = bar + 1; // should be illegal: it can be hard to distinguish if `bar` is a local variable versus an instance member If it was this->bar it could be a member, it could also be a static variable. A bar on its own could be local or it could be in any of the enclosing scopes or namespaces. Forcing "this" to be explicit doesn't make the code any clearer on its own.

The guy was referring to the explicit case where bar is a member variable. The cases where it is in the local scope under scoping rules or the global scope are not really an issue, since you can check the function definition to find if it is local. In the case that it is not in the function definition, then it is in the global scope in C. If implicit this were not done in C++, that would also be the case in C++, provided you do the sane thing and use the std namespace for everything. Just thinking about the headaches namespaces could cause when looking for such definitions with cscope gives me yet another reason to stay away from C++ whenever possible.

Re: Object-oriented design patterns in C and kernel development

#128

Earlier quoted context omitted.

Agreed, one of the biggest design mistakes in the OOP syntax of C++ (and Java, for that matter) was not making `this` mandatory when referring to instance members.

C++ and Java went for the "objects as static closures" route, where it doesn't make any sense to have a `this`. Or, they made them superficially look like static closures, which in hindsight was probably not the best idea. Anyway, Java lets you use explicit `this`, I don't recall whether C++ makes it into a footgun or not.

Supporting an explicit this is required to be able to access the member variable when a local variable hides it under scoping rules. As another person replied, C++ does indeed have an implicit this.

Re: Object-oriented design patterns in C and kernel development

#129
post #104
post #47

Earlier quoted context omitted.

Nothing a little macro magic couldn't fix.. #define CALL(object, function, ...) (object->ops->function(object, __VA_ARGS__))

You can just use C++ instead of reinventing a worse version of C++98!

It would be an improvement on C++ since you would avoid the horrendous error messages from ridiculously long types, slow compile times, exception handling nightmares, bloat from RTTI and constant worry that operators might not mean what you expect. That is before even mentioning entirely new classes of problems like the diamond problem. Less is more.

That said, similar macro magic is used in C generic data structures and it works very well.

Re: Object-oriented design patterns in C and kernel development

#130
post #16

> Having to pass the object explicitly every time feels clunky, especially compared to C++ where this is implicit. I personally don't like implicit this. You are very much passing a this instance around, as opposed to a class method. Also explicit this eliminates the problem, that you don't know if the variable is an instance variable or a global/from somewhere else.

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).

What guarantee do you have that ->ops is a vtable? It could contain function pointers that don’t take an implicit this argument like struct file_operations in Linux does. It could also contain variables that are non-pointers. Neither is allowed in a vtable, but both are fine to do in C.
Post reply on HN