14 ms·
OOP in C
- johndoe0815 4y agoA whole book on this topic was already written in 1993 by Axel-Tobias Schreiner. It seems to be freely available nowadays: https://www.cs.rit.edu/~ats/books/ooc.pdf https://www.cs.rit.edu/~ats/books/ooc.pdf (edit: link updated to point to author's website)
- askvictor 4y agoI remember having a book on the shelf through the 90s entitled 'Object Oriented Programming in Macro Assembler'
- friendlyHornet 4y agoI 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
- deleted 4y ago[deleted]
- linkdd 4y agoYears ago, I started an implementation of that book: https://github.com/linkdd/ooduck https://github.com/linkdd/ooduck Never truly finished it, but it works well :)
- pmontra 4y agoThat 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.
- attractivechaos 4y agoThe 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 https://www.cs.rit.edu/~ats/books/ooc-14.01.03.tar.gz
- sfpotter 4y agoThis 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. :-)
- quelsolaar 4y agoI 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....
- tpoacher 4y agoI 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.
- linkdd 4y agoYou mean "C with classes" ?
- KingLancelot 4y ago[dead]
- tpoacher 4y agonope, it's this book: https://www.goodreads.com/book/show/30040029-object-oriented-programming-with-ansi-c https://www.goodreads.com/book/show/30040029-object-oriented... i was basically reiterating my comment from there.
- kasajian 4y agoSimilar to: https://news.ycombinator.com/item?id=34297450 https://news.ycombinator.com/item?id=34297450
- zabzonk 4y agoall that memory map stuff is completely out of whack, along with much else
- deleted 4y ago[deleted]
- throw_m239339 4y ago[flagged]
- twawaaay 4y agoHi. For background, I have been writing professional OOP code in ANSI C. Yes, Structures in C can be used as classes. And yes, this includes polymorphism. My solution was to put a callback in the structure that would point to the implementation that was polymorphic. This allowed me to pass the struct around and then call the specific implementation at the right time. When you write OOP in C you do not need to make it look like OOP language. You just need to understand what OOP is about and use the existing language to do what you need it to do as efficiently as possible. As to spending time on writing functionality that is readily available in other languages... sometimes you just don't have a choice. There are programming environments where ANSI C is the only language available as was in my case. Please, try not to be too dismissive to the things you do not understand.
- zabzonk 4y agoplease post an example of what such a callback would look like
- rramadass 4y agoOne example: https://www.codementor.io/@michaelsafyan/object-oriented-programming-in-c-du1081gw2 https://www.codementor.io/@michaelsafyan/object-oriented-pro...
- cihangir 4y agoLinux kernel code makes heavy use of object-oriented design patterns [0]. It's not the best of its kind, but still provides a reasonable example of how OOP can be achieved in C: https://gist.github.com/cakturk/cd75d0ca588151c86d641cb6d5a18153 https://gist.github.com/cakturk/cd75d0ca588151c86d641cb6d5a1... [0] https://lwn.net/Articles/444910/ https://lwn.net/Articles/444910/
- 4y ago
- bni 4y agoADTs was a major part of the C course I took in 1998
- zabzonk 4y agoso-called abstract data types have nothing really to do with OO programming
- wheelerof4te 4y agoI was thinking the same thing.
- ChrisMarshallNY 4y agoI 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.
- MikePlacid 4y agoIn 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.HTML 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…
- antirez 4y agoC 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.
- bullen 4y agoI 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.
- michaelcampbell 4y agoI 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.
- mpardalos 4y agoUnrelated to the content, but this is basically a perfect webpage.
- redgrange 4y agoI see so many uses of the term ADT that I can't really give anyone a confident definition
- cormacrelf 4y agoUsing 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.
- marcosdumay 4y ago> 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".
- cormacrelf 4y agoWe should call it an opaque data type then.
- marcosdumay 4y ago
- anthomtb 4y agoStrongswan, the open source IPSec VPN software (https://www.strongswan.org/ https://www.strongswan.org/) is written in object oriented C. Ref: https://docs.strongswan.org/docs/5.9/devs/objectOrientedC.html 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.
- _notreallyme_ 4y agoI 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.
- znpy 4y agoBeside the topic or the content, I love the typography choices of this.
- dboreham 4y agoOk, 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 4y ago[deleted]
- ryanianian 4y agoTLDR: 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.
- JonChesterfield 4y agoYou'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 4y ago[deleted]
- kevin_thibedeau 4y agoHand 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.
- _0w8t 4y agoI 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.
- flohofwoe 4y agoIt 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.
- _0w8t 4y agoMy experience was with MFC, that uses methods even for basic getters.
- carapace 4y agoOr you could implement COLA? https://en.wikipedia.org/wiki/COLA_(software_architecture) 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/ https://piumarta.com/software/cola/ Or check out the VPRI reports, etc.: https://web.archive.org/web/20220819075633/https://www.vpri.org/writings.php https://web.archive.org/web/20220819075633/https://www.vpri.... Ironically their website appears to be down at the moment.
- hgs3 4y agoThere 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 https://en.wikipedia.org/wiki/Objective-C [2] https://github.com/orangeduck/Cello https://github.com/orangeduck/Cello
- Congeec 4y agoThe famous htop actually employs OOP in C. https://github.com/htop-dev/htop/blob/650cf0f13bf667270d0a6a4612437af264667585/AffinityPanel.c#L32-L57 https://github.com/htop-dev/htop/blob/650cf0f13bf667270d0a6a...
- mananaysiempre 4y agoSo 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.)
- posharma 4y agoWhile 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.
- oso2k 4y agoYou can do RAII in C. You still have to define a dtor function. https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization#Compiler_%22cleanup%22_extensions https://en.wikipedia.org/wiki/Resource_acquisition_is_initia...
- matiasfernandez 4y agoI 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.
- WalterBright 4y agoI'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.
- ikari_pl 4y agothe implementation in the article is basically what Golang is. and the question: "so much manual/repetitive work, why do this"... is still valid.
- WalterBright 4y agoYup. 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.
- ActorNightly 4y agoThe 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.