4 ms·
C required the simplest form of OOP, but who standardizes it decided to deny the feature to the language: the ability to have callable structure methods (functi
by antirez 4y ago
C required the simplest form of OOP, but who standardizes it decided to deny the feature to the language: the ability to have callable structure methods (function pointers) with explicit "self" pointer. Not even constructors/destructors. After all it's C, and you can write list->init() and list->free(). This simple form to bind data and the functions operating on such data would make many codebases better.
- Vogtinator 4y agoThat wouldn't actually help much. In the case that there is no inheritance, using a function pointer has unnecessary overhead (pointer storage, indirect call, additional parameter). In the case of inheritance, the pointer type differs and thus the function pointer type wouldn't be compatible either.
- zozbot234 4y agoYou need to use a void* self pointer. And indirect through the vtable in all calls to virtual methods, even those that are part of the object itself. Thus, e.g. a method defined on a base class might end up calling an implementation deep in the inheritance hierarchy, that didn't even exist when the base class code was written. There's no real equivalent to this when doing simple interface inheritance, but it's very much part of the actual semantics here.
- deleted 4y ago[deleted]
- alerighi 4y agoInheritance is nowadays almost considered a bad practice, in fact newer languages, such as Rust, doesn't support it, and in languages that has it (Java, C#, etc) nowadays they always tell you to prefer composition over inheritance. > using a function pointer has unnecessary overhead This is true, it's inefficient. But for a lot of application, also irrelevant at performance level, and would provide a good abstraction.
- zozbot234 4y agoAIUI you can implement inheritance as a pattern on top of existential types, using a self-recursive definition trick. Not sure if Rust has full existential types yet, but they're definitely a goal since they're needed e.g. for better async support.
- queuebert 4y agoRust has a flavor of inheritance through Traits and #derive.
- zozbot234 4y agoTraits implement interface inheritance, yes. #derive has nothing to do with derived classes in OOP, it's an annotation-like facility that adds auto-generated code to any object.
- queuebert 4y agoIsn't #derive a syntactical sugar for a bare impl?
- alerighi 4y agoNo, traits are not inheritance, nor is derive that is default implementation of some traits. Traits are like interfaces in OOP languages (simplified), the equivalent of a class would be a struct + all the impls of that struct. And that thing cannot be extends, a struct is fixed when defined and nobody can extend it, you can use composition that is the correct to me way to go.
- queuebert 4y agoThat's why I said "flavor". It's not exactly inheritance obviously, but provides some of the same functionality. There are many ways to skin an OO cat. Traits describe the methods implemented, and you can override with specific implementations or inherit the default, from a level up.
- 4y ago
- cihangir 4y agoLast time I checked, C++ used function pointers behind the scenes to achieve virtual method dispatch.
- kllrnohj 4y agoTypically the object contains a single pointer to the vtable which is then the list of function pointers. You can do this structure in C, but more common is the struct of function pointers which avoids a level of indirection at the cost of object size.
- deleted 4y ago[deleted]
- AlbertoGP 4y agoI made a pre-processor [1] to add similar features to C, and after reading your comment I’m thinking that it would be simple to add a setting/#pragma to do these transformations: list->init(); → list->init(list); list.init(); → list.init(&list); [1] https://sentido-labs.com/en/library/cedro/202106171400/#backstitch-macro https://sentido-labs.com/en/library/cedro/202106171400/#back... I normally avoid the function pointer overhead, which can be done with _Generic: #define append(VEC, START, END) _Generic((VEC), \ Vec_float*: append_Vec_float, \ Vec_str*: append_Vec_str, \ Vec_cstr*: append_Vec_cstr \ )(VEC, START, END) https://sentido-labs.com/en/library/cedro/202106171400/#loop-macros-vec-example https://sentido-labs.com/en/library/cedro/202106171400/#loop... But list->init(list) might be a simpler solution for most cases, and compatible with C89/C99.
- AlbertoGP 4y agoI’ve built it for now in a separate branch called “self”: git clone -b self https://github.com/Sentido-Labs/cedro.git cd cedro make bin/cedro # Just “make” will build cedrocc etc. bin/cedro - <<' EOF' # Mind the indentation. #pragma Cedro 1.0 self list->init(); list.init(); list->append(123); list.append(123); EOF The “self” flag after “#pragma Cedro 1.0” activates the “self” macro, because it should not be done by default. Result: list->init(list); list.init(&list); list->append(list, 123); list.append(&list, 123); I’ll try it out for a few days and if it works well in practice I’ll document it and merge it into master.
- Koshkin 4y ago> I made a pre-processor You followed the path of C++.
- billfruit 4y agoBut how do you tackle creation of objects, call malloc everytime? And how do you for example, create an array of objects, like one can easily do in c++?
- deleted 4y ago[deleted]
- deleted 4y ago[deleted]