OOP in C(staff.washington.edu)
staff.washington.edu
OOP in C
http://staff.washington.edu/gmobus/Academics/TCES202/Moodle/OO-ProgrammingInC.html
147 comments
I started reading rhis last year in my free time, and following along coding myself.
I only read a few chapters then got sidetracked, but man I learnt so much stuff from reading just a bit
i already knew all the C concepts he was using but it never occurred to me to use them in that way.
The book is very well-written and easy to understand, and also very approachable even for someone who is not so experienced with C, like myself
I want to start over someday
I only read a few chapters then got sidetracked, but man I learnt so much stuff from reading just a bit
i already knew all the C concepts he was using but it never occurred to me to use them in that way.
The book is very well-written and easy to understand, and also very approachable even for someone who is not so experienced with C, like myself
I want to start over someday
[deleted]
That was how I was doing OOP in C in the late 80s and early 90s. I found many of those concepts both in Python and in JavaScript. After all they were born around that time.
Python because of the insistence of declaring that explicit self argument, the pointer to the struct for the object.
JavaScript because of the prototype based OO. You can change the meaning of any field in an OO C struct if you know how to handle it later.
Python because of the insistence of declaring that explicit self argument, the pointer to the struct for the object.
JavaScript because of the prototype based OO. You can change the meaning of any field in an OO C struct if you know how to handle it later.
Years ago, I started an implementation of that book: https://github.com/linkdd/ooduck
Never truly finished it, but it works well :)
Never truly finished it, but it works well :)
Fyi, fwiw, the top-of-page "About Duck-Typing C library based on ooc.pdf linkdd.github.com/ooduck/ " link is currently 404.
Ah thank you, it predates Github's renaming of Github Pages from .com to .io --> https://linkdd.github.io/ooduck/
The webpage in the title is pretty good. It explains most things you need to achieve OOP in C. This PDF however pushes C for what it is not intended for, which leads to inefficient and obscure code.
For example, the first example in the PDF implements a Set. Everything is "void". This loses typing and will make code hard to read and maintain. The way inheritance is mimicked is hideous and inefficient. The PDF also heavily uses function pointers, which can be slow (because they aren't easily inlined) and waste memory (because each pointer costs 8 bytes on x64).
In all, most large C projects are doing OOP at some level. However, C is not C++ and is not intended for providing all OOP features like inheritance. This book should probably be titled "Wrong ways to OOP in C".
PS: The book does have some interesting ideas, but those are the "clever" things you will have to unlearn later. The author provides the source code for the book [1]. Have a look at the string implementation in Chapter Two. That is the most arcane and inefficient string implementation I have seen.
[1] https://www.cs.rit.edu/~ats/books/ooc-14.01.03.tar.gz
For example, the first example in the PDF implements a Set. Everything is "void". This loses typing and will make code hard to read and maintain. The way inheritance is mimicked is hideous and inefficient. The PDF also heavily uses function pointers, which can be slow (because they aren't easily inlined) and waste memory (because each pointer costs 8 bytes on x64).
In all, most large C projects are doing OOP at some level. However, C is not C++ and is not intended for providing all OOP features like inheritance. This book should probably be titled "Wrong ways to OOP in C".
PS: The book does have some interesting ideas, but those are the "clever" things you will have to unlearn later. The author provides the source code for the book [1]. Have a look at the string implementation in Chapter Two. That is the most arcane and inefficient string implementation I have seen.
[1] https://www.cs.rit.edu/~ats/books/ooc-14.01.03.tar.gz
I have not read the book.
> However, C is not C++ and is not intended for providing all OOP features like inheritance.
There are parts of the ISO C standard specifically included to allow for inheritance. Consider:
typedef struct{ int x; }struct_a;
typedef struct{ struct_a inherited; int y; }struct_b;
void function(struct_a *s) { s->x = 0; }
Here struct_b inharates struct_a. You can call the function with either a pointer to struct_a or struct_b (either by casing or void pointer), because the standard specifically states that there can be no padding before the first member of a structure, so that the first member of a structure should have the same pointer as the structure itself. This part of the spec was written with the use case of inheritance in mind. (there is some people who don't read the spec that way, but this is the intention)
Sorry for being nitpicky... but that's pretty much what we do in the wg14....
> However, C is not C++ and is not intended for providing all OOP features like inheritance.
There are parts of the ISO C standard specifically included to allow for inheritance. Consider:
typedef struct{ int x; }struct_a;
typedef struct{ struct_a inherited; int y; }struct_b;
void function(struct_a *s) { s->x = 0; }
Here struct_b inharates struct_a. You can call the function with either a pointer to struct_a or struct_b (either by casing or void pointer), because the standard specifically states that there can be no padding before the first member of a structure, so that the first member of a structure should have the same pointer as the structure itself. This part of the spec was written with the use case of inheritance in mind. (there is some people who don't read the spec that way, but this is the intention)
Sorry for being nitpicky... but that's pretty much what we do in the wg14....
Doesn't this violate strict aliasing? E.g. what if we do this?
void foo(struct_a *a, struct_b *b) { a->x = 0; b->x += 1; a->x = 0; }
Strict aliasing allows the compiler to elide the second a->x = 0. But if we use your inheritance scheme using casting of pointers, we could have a == b.This book is a really fun read. You’ll learn a lot and get some interesting ideas.
Don’t listen to prescriptions from strangers on HN, how about? Myself included. :-)
Don’t listen to prescriptions from strangers on HN, how about? Myself included. :-)
I remember having a book on the shelf through the 90s entitled 'Object Oriented Programming in Macro Assembler'
This sounds like a nightmare
I wasn't too keen on this book. I read it hoping it would be more like the article in this post. But I felt it was more a case of someone re-inventing c++ with macros.
You mean "C with classes" ?
nope, it's this book: https://www.goodreads.com/book/show/30040029-object-oriented...
i was basically reiterating my comment from there.
i was basically reiterating my comment from there.
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.
I 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:
I normally avoid the function pointer overhead, which can be done with _Generic:
But list->init(list) might be a simpler solution for most cases, and compatible with C89/C99.
list->init(); → list->init(list);
list.init(); → list.init(&list);
[1] 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...But list->init(list) might be a simpler solution for most cases, and compatible with C89/C99.
I’ve built it for now in a separate branch called “self”:
Result:
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.> I made a pre-processor
You followed the path of C++.
You followed the path of C++.
> You followed the path of C++.
To some extent yes, I know about cfront.
This is just another iteration on that old idea.
To some extent yes, I know about cfront.
This is just another iteration on that old idea.
Sure; you’re basically implementing The prehistoric version of C++, called “C with Classes.” If you’re unaware, see Stroustrup’s Design and Evolution of C++ book.
Which is fine! Should be interesting.
Which is fine! Should be interesting.
[deleted]
That 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.
You 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]
Inheritance 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.
> 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.
Inheritance is the best solution to some problems.
It got a bad reputation because it was abused. Bad practice is using inheritance when a tuple or a map would suffice.
It got a bad reputation because it was abused. Bad practice is using inheritance when a tuple or a map would suffice.
To some problems that are almost always seen in the academic world but I've yet to see in my daily job.
Inheritance (from implementation, I've nothing against implementation of interfaces or inheritance from abstract base classes) has a ton of problems, more importantly the fact that it makes the code more difficult to understand and to evolve.
Composition on the other hand is something more natural, even if we think about real life: you don't usually take an object and "extend" it, you take multiple object and use them together to build something!
Inheritance (from implementation, I've nothing against implementation of interfaces or inheritance from abstract base classes) has a ton of problems, more importantly the fact that it makes the code more difficult to understand and to evolve.
Composition on the other hand is something more natural, even if we think about real life: you don't usually take an object and "extend" it, you take multiple object and use them together to build something!
It's strange that you seem to be an absolutist on this topic. What is wrong if inheritance is an elegant solution to a relatively small number of problems? Historical overuse of inheritance doesn't take away from that.
What problem is left? UI? React is showing composition wins there too.
It's just a code reuse mechanism. In some cases you can do with it what you might also do with callbacks, or yes, composition. Or in some cases inheritance might be handy.
I don't know why we need to be so judgmental about it.
I think inheritance is especially good if you have an interface (in the OO sense, like some languages use an "interface" keyword for), but you have some common or default methods, which a specific implementation may or may not override, or maybe there is some boiler plate or tedium where the most common implementation might belong in a base class. I think this is handy for something like a device driver.
I don't know why we need to be so judgmental about it.
I think inheritance is especially good if you have an interface (in the OO sense, like some languages use an "interface" keyword for), but you have some common or default methods, which a specific implementation may or may not override, or maybe there is some boiler plate or tedium where the most common implementation might belong in a base class. I think this is handy for something like a device driver.
> has a ton of problems
this may be a good reference with examples (and some good dose of nuance) on the subject:https://blog.gougousis.net/oop-inheritance-what-when-and-why...
The main issue with inheritance is hard coupling of data and code. As code evolve from data modeling point of view it can be advantages to split the state managed by one object into several data structures. With data inheritance such refactoring is almost impossible. When inheritance is limited only to pure interfaces the access to the state is not hard-coded and can be redirected as necessary.
AIUI 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.
Rust has a flavor of inheritance through Traits and #derive.
No, 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.
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.
That'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.
Traits describe the methods implemented, and you can override with specific implementations or inherit the default, from a level up.
> prefer composition over inheritance
Right... There is an animal in a dog.
Right... There is an animal in a dog.
Last time I checked, C++ used function pointers behind the scenes to achieve virtual method dispatch.
Typically 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.
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.
But 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++?
And how do you for example, create an array of objects, like one can easily do in c++?
[deleted]
[deleted]
Or you could implement COLA?
https://en.wikipedia.org/wiki/COLA_(software_architecture)
Huh, that page was deleted in Dec 22. "concern was: Old research project. Unable to locate any details. VPRI institute is dead. Been on the cat:nn list since March 2009. No new updates."
Goddamned deletionist activist WP editors tearing down the human knowledge base.
(If you don't know VPRI is (was?) Alan Kay's research org. I think it's a bit notable and important.)
ANYWAY
From the search result (in DDG) snippet I can get the first two sentences of the deleted page:
> "COLA" stands for "Combined Object Lambda Architecture". [1] A COLA is a self-describing language in two parts, an object system which is implemented in terms of objects, and a functional language to describe the computation to perform. [2]
It's a very simple system that gives you the basis for both OOP and Lisp-like semantics. It's fun!
You can see it here: https://piumarta.com/software/cola/
Or check out the VPRI reports, etc.:
https://web.archive.org/web/20220819075633/https://www.vpri....
Ironically their website appears to be down at the moment.
https://en.wikipedia.org/wiki/COLA_(software_architecture)
Huh, that page was deleted in Dec 22. "concern was: Old research project. Unable to locate any details. VPRI institute is dead. Been on the cat:nn list since March 2009. No new updates."
Goddamned deletionist activist WP editors tearing down the human knowledge base.
(If you don't know VPRI is (was?) Alan Kay's research org. I think it's a bit notable and important.)
ANYWAY
From the search result (in DDG) snippet I can get the first two sentences of the deleted page:
> "COLA" stands for "Combined Object Lambda Architecture". [1] A COLA is a self-describing language in two parts, an object system which is implemented in terms of objects, and a functional language to describe the computation to perform. [2]
It's a very simple system that gives you the basis for both OOP and Lisp-like semantics. It's fun!
You can see it here: https://piumarta.com/software/cola/
Or check out the VPRI reports, etc.:
https://web.archive.org/web/20220819075633/https://www.vpri....
Ironically their website appears to be down at the moment.
While I commend this effort I should say that seriously, just write C++ if possible. No matter what you do memory management is going to be your biggest enemy (I’m ignoring the aesthetic aspects like macros, function pointers, type safety, etc). Without RAII it’s just not worth it. Just choose the simpler portions of C++ and keep safe.
I never have trouble with memory management in C by sticking to "grouped element architectures" with explicit lifetimes such as arenas, scratch allocators, dynamic pools, etc. RAII is mostly for "single element architectures" which IMO are not a good way to organize code because they tend to amount to thousands of unnecessary memory operations.
You can do RAII in C. You still have to define a dtor function.
https://en.wikipedia.org/wiki/Resource_acquisition_is_initia...
https://en.wikipedia.org/wiki/Resource_acquisition_is_initia...
Yeah, I feel like “C in a C++ Compiler” is a respectable decision for reasons similar to what you describe.
There is a lightweight object oriented extension to C called Objective-C [1] that unfortunately never gained much traction outside the NeXT/Apple ecosystem. There is also Cello [2].
[1] https://en.wikipedia.org/wiki/Objective-C
[2] https://github.com/orangeduck/Cello
[1] https://en.wikipedia.org/wiki/Objective-C
[2] https://github.com/orangeduck/Cello
I was surprised no one here mentioned cfront. My first job out of college I would look at the generated C code from my C++ source to see how it would convert some of the the C++ things into C, since I couldn't figure out how to do it myself.
The famous htop actually employs OOP in C. https://github.com/htop-dev/htop/blob/650cf0f13bf667270d0a6a...
So does Glibc in its stdio implementation (bog-standard vtables except the vtable pointer can occasionally change at runtime), Linux in a number of places (I don’t really know much there), the FreeBSD kernel in its driver ABI (more like ObjC than C++) and many others.
(GObject is of course also object-oriented, but IMO hardly counts as C programming in how it works, it’s more of a separate OO language hand-translated into C—Vala is basically that language, finally implemented years later.)
(GObject is of course also object-oriented, but IMO hardly counts as C programming in how it works, it’s more of a separate OO language hand-translated into C—Vala is basically that language, finally implemented years later.)
I was doing OOP in C, back in the early ‘90s. Lotta work, but turned out great. Some of the software that I wrote back then, was still in use, 25 years later (a camera software SDK).
It had to be done a certain way, because stack conventions were all over the place, back then.
It had to be done a certain way, because stack conventions were all over the place, back then.
In early 90s we’ve got a CD-ROM with all known genomic data from NCBI. And on the same CD there was a portable (Win, Macintosh, X Win) graphics system written in C in OOP style, named Vibrant. Portable graphics long before Java! And using that funny - and very usable - OOP style too. So, instead of studying genomes I was looking at C code, and soon was using it in a telecom project that I was doing on the side (90s were pretty hard in Russia).
https://www.ncbi.nlm.nih.gov/IEB/ToolBox/SDKDOCS/VIBRANT.HTM...
The code is pretty well written and probably can be used in a teaching environment, like the original article describes. But I am very bad in teaching…
https://www.ncbi.nlm.nih.gov/IEB/ToolBox/SDKDOCS/VIBRANT.HTM...
The code is pretty well written and probably can be used in a teaching environment, like the original article describes. But I am very bad in teaching…
I recently needed to write C code to interface with a COM library on Windows. While Microsoft fully supports it, the result is very foreign compared with typical C code.
Surely in C one does use tables of function pointers to implement some aspects of OOP. However, a good library limits it and rather exposes some structs instead of requiring multiple virtual methods calls to archive a similar effect.
Surely in C one does use tables of function pointers to implement some aspects of OOP. However, a good library limits it and rather exposes some structs instead of requiring multiple virtual methods calls to archive a similar effect.
It depends a lot on the wrapped APIs, for instance D3D11 is a struct-heavy API exposed via COM and totally fine being used from C code (instead of C++), even works nicely with C99 designated init for initializing the input structs.
My experience was with MFC, that uses methods even for basic getters.
I think arrays of 64 byte structures is the most optimal way to architect your multicore "on the same memory" code.
Preferably using int/float which should be atomic on X86 and hopefully on modern ARM too, don't know about RISC-V yet.
Preferably using int/float which should be atomic on X86 and hopefully on modern ARM too, don't know about RISC-V yet.
Strongswan, the open source IPSec VPN software (https://www.strongswan.org/) is written in object oriented C. Ref: https://docs.strongswan.org/docs/5.9/devs/objectOrientedC.ht...
I have not looked through the source myself but thought this might be an interesting reference.
I have not looked through the source myself but thought this might be an interesting reference.
I can confirm that indeed Strongswan is written in OOP C.
This is relatively common in software with a modular architecture that is intended to run on embedded systems and whose design dates back from a time when C++ standard library was not really well supported on such systems. This is also useful when performance is highly important and you can't afford the C++ vtable overhead.
This is relatively common in software with a modular architecture that is intended to run on embedded systems and whose design dates back from a time when C++ standard library was not really well supported on such systems. This is also useful when performance is highly important and you can't afford the C++ vtable overhead.
I've seen OOP inheritance in C before. It involves a table of pointers to functions, and a derived "class" would just point to a different table.
It works, but all the machinery has to be handled manually. It's very, very easy to make a mistake. The question is, why do this? Just use C++ as "C with classes" and you'll be much better off.
It works, but all the machinery has to be handled manually. It's very, very easy to make a mistake. The question is, why do this? Just use C++ as "C with classes" and you'll be much better off.
The goal of this is to understand how OOP works under the hood, so you can adapt it to your own use case. You can do more strict OOP than C++ if you wanted to. However in most cases, all you need is basic OOP, namely writing your code with clean separation of functionality in different files with `static` keyword being similar to what `private` means in C++, and then using structs with typedef to define data. Function pointers and additional macros just add syntactic sugar, and are not necessary.
The main advantage of C++ isn't really OOP, its all the standard library features and smart pointers, but those are not always applicable (like when you want to manage memory as efficient as possible). So using C is often preferred.
The main advantage of C++ isn't really OOP, its all the standard library features and smart pointers, but those are not always applicable (like when you want to manage memory as efficient as possible). So using C is often preferred.
the implementation in the article is basically what Golang is.
and the question: "so much manual/repetitive work, why do this"... is still valid.
and the question: "so much manual/repetitive work, why do this"... is still valid.
Yup. Once you find yourself doing OOP in C, or doing metaprogramming with the C preprocessor, it's time to move to a more powerful language. You've outgrown C.
I see so many uses of the term ADT that I can't really give anyone a confident definition
Using ADT to mean “abstract data type” does not mean anything more than what most people mean when they say “type”. Outside of distinguishing from other meanings of the word “type” there is no practical reason to ever say it. It doesn’t sound fancy, it just sounds like getting high on your own supply of acronyms. It is not an important term to learn as a beginner in the first chapters of a programming book. We should stop using it in resources like this.
Otherwise, ADT can also refer to “algebraic data type” which means the ability to compose types together thereby adding or multiplying the different values the resulting type can take. Product types (aka structs) multiply over their fields; two int fields when taken together can take on (# different values of int) * (# different values of int) different values. Sum types add over their variants: a Rust enum “enum OptionalInt { Some(i32), Maybe(i32), None }” can take (# different values of i32) + (# different values of i32) + 1 different values.
Most languages have structs and that’s the “product type” sorted, so ADT generally refers to a language having tagged enums. C doesn’t have that but you can emulate it (quite badly) with unions and enums. Good examples of languages with ADTs are OCaml, Haskell, Rust.
Otherwise, ADT can also refer to “algebraic data type” which means the ability to compose types together thereby adding or multiplying the different values the resulting type can take. Product types (aka structs) multiply over their fields; two int fields when taken together can take on (# different values of int) * (# different values of int) different values. Sum types add over their variants: a Rust enum “enum OptionalInt { Some(i32), Maybe(i32), None }” can take (# different values of i32) + (# different values of i32) + 1 different values.
Most languages have structs and that’s the “product type” sorted, so ADT generally refers to a language having tagged enums. C doesn’t have that but you can emulate it (quite badly) with unions and enums. Good examples of languages with ADTs are OCaml, Haskell, Rust.
> Using ADT to mean “abstract data type” does not mean anything more than what most people mean when they say “type”.
Abstract data type is a type where you don't get direct access to the information contained in it. The encapsulation is what makes it "abstract".
Abstract data type is a type where you don't get direct access to the information contained in it. The encapsulation is what makes it "abstract".
We should call it an opaque data type then.
That thing was named much before the other ADT became popular. The name is spread over a lot of past texts, and not in wide use anymore.
There's no point in changing anything.
There's no point in changing anything.
Is a Java ArrayList considered an ADT like stack, queue, set, etc?
In the context of C programming, ADT usually means an opaque struct with some related functions.
An abstract data type (ADT) is a structured type for which a client cannot access the data components for variables of this type. The client understands the type in terms of its operations, provided by a set of functions which operates on variables of this type. The set of functions is called the interface of the ADT. Note that this definition requires information hiding but not inheritance or polymorphism.
How would you distinguish that definition from one for "type"?
> How would you distinguish that definition from one for "type"?
The crucial difference between an abstract data type and a concrete data type is that the content is hidden in the former case.
The crucial difference between an abstract data type and a concrete data type is that the content is hidden in the former case.
Beside the topic or the content, I love the typography choices of this.
TLDR: Put data members in a struct. Write a function per "method" and pass in the `this` pointer. Simple and effective.
BUT: inheritance. C++ has subclasses (`class Manager : Employee`) and virtuals / vtable lookup on pointer access (`employee->name()` which calls `Manager::name` or `Employee::name` based on type).
What is the typical equivalent in C? Hand-rolled vtables? Or is there a general paradigm that helps to avoid the need for it in C?
I ask as a seasoned C++ and Java developer looking to improve some C code which uses this pattern in the extreme and needs some refactoring. My intuition is composition rather than inheritance, although isA is more intuitive than hasA for this code-base.
BUT: inheritance. C++ has subclasses (`class Manager : Employee`) and virtuals / vtable lookup on pointer access (`employee->name()` which calls `Manager::name` or `Employee::name` based on type).
What is the typical equivalent in C? Hand-rolled vtables? Or is there a general paradigm that helps to avoid the need for it in C?
I ask as a seasoned C++ and Java developer looking to improve some C code which uses this pattern in the extreme and needs some refactoring. My intuition is composition rather than inheritance, although isA is more intuitive than hasA for this code-base.
Hand rolled vtables in a struct that has metadata for the class. SQLite has a nice system where the class definition struct carries the size of the base class and any additional members so that objects can be allocated from common code and passed to their constructor method.
You're going to think this is a joke but I'm mostly sincere The right refactor is to untangle the data structures, write a lua interface to them, then port the application logic to lua. Since (one of the) lua is written in C your codebase is still all C.
[deleted]
ADTs was a major part of the C course I took in 1998
so-called abstract data types have nothing really to do with OO programming
I was thinking the same thing.
Unrelated to the content, but this is basically a perfect webpage.
Ok, but this is how pretty much all large programs were designed in assembler. C has the data structures commonly used in assembler. C++ (originally) removes some boilerplate and adds some comple-time checking.
[deleted]
https://www.cs.rit.edu/~ats/books/ooc.pdf
(edit: link updated to point to author's website)