Live data from Hacker News

OOP in C

staff.washington.edu

51–60 of 149 posts

Re: OOP in C

#51
post #30

Earlier quoted context omitted.

Trivial to a C developer then. This has been typical of C code for literally decades. The only thing that sucks there is writing wrappers, but it's easy to generate them. From a user point of view you end up doing things like: string * str = string_new(); int len = string_length(str); So it looks just like most other typical C code using prefixes per interface and passing pointers to structs around. In practice, like…

"int string_length(void * self) { return ((int ( )(void ))((object *)self)->class->vtable[STRING_LENGTH_SLOT])(self);" Oh.My.God. At this point, you might as well be using Java.

Or C++

/gasp

Re: OOP in C

#52

A 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 (edit: link updated to point to author's website)

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.

Re: OOP in C

#53

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.

Re: OOP in C

#54

A 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 (edit: link updated to point to author's website)

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

[deleted]

Re: OOP in C

#55

Earlier quoted context omitted.

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.

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…

Rust has a flavor of inheritance through Traits and #derive.

Re: OOP in C

#56

[flagged]

I believe the author was trying to drive the point that structs in C are the most similar language feature to classes in Java rather than implying the two are identical. Perhaps the title could’ve have been written less absolutely, but declaring the entire article as “terrible” simply because a single title was unclear is simply an exaggerated criticism.

Re: OOP in C

#57
post #37

Earlier quoted context omitted.

"int string_length(void * self) { return ((int ( )(void ))((object *)self)->class->vtable[STRING_LENGTH_SLOT])(self);" Oh.My.God. At this point, you might as well be using Java.

If you're writing C, you'll be writing much worse than that on a regular basis. There's a reason I rarely use C any more. But of course if you're going to reimplement OO from scratch in C you will see the entire machinery laid bare. You're only doing it in the first place either because you're committed to using C for whatever reason, or to understand OO implementation strategies.

Replying to myself to leave this here, since it's vaguely related. If anyone really want to have nightmares about OO'ish concepts in C, here is a few different ways to implement closures in C, going from relatively straightforward, to (unportable) runtime generation of assembler thunks:

https://hokstad.com/how-to-implement-closures

Re: OOP in C

#58
post #21
post #16

Earlier quoted context omitted.

Polymorphism in C is trivial: You use a vtable - a struct of method pointers. Same approach most languages with "simple" method resolution mechanisms, like e.g C++, uses under the hood. You don't need macros. You either call via the vtable, or you write a wrapper per interface per message. E.g. struct class { void (*vtable[NUM_SLOTS]); }; struct object { struct class * class; }; typedef struct object object; const in…

what does an "object" look like? how do i derive from it?

Having spent quite some time down that rabbit hole back in the days, I'd definitely recommend against pushing C in that direction. Few people love GObject, for good reasons.

Embedding combined with the container_of-macro [0] is a much more constructive approach from my experience.

[0] https://stackoverflow.com/questions/15832301/understanding-c...

Re: OOP in C

#59

Earlier quoted context omitted.

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…

Rust has a flavor of inheritance through Traits and #derive.

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

Re: OOP in C

#60
post #12

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.

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.

Last time I checked, C++ used function pointers behind the scenes to achieve virtual method dispatch.
Post reply on HN