Polymorphism with Go-like interfaces in C?

I'm writing a ray tracer based on Peter Shirley's "Ray tracing in one weekend" book. The code in the book is written in C++ and he used virtual functions (ugh) to delegate the task of computing if an object in the scene has been hit by a ray to the object themselves. I rewrote it in Go, and the interfaces there are so much better to work with, because of the real separation between interface and implementation. Now I'd like to rewrite it in C, but how should I deal with polymorphism? I ask here because maybe someone came up with a simple solution which works in general, when you have to implement polymorphism in C.


Edited by escalioth on

You can have structs of function pointers replacing the virtual calls. Ideal for the strategy pattern, because you can have multiple behaviors in the same object, as opposed to allocating a new object for each set of functions. If the classes need different variables and you don't want to reuse them, you can use a pointer to an arbitrarily sized buffer and allocate it on construction. Reusing variables will however allow you to allocate fixed size memory on the stack and change functions individually at runtime without resetting the object's state, which is not possible in strict object orientation where you only store a fixed virtual function table per class. The CPython reference implementation relies heavily on structs of function pointers for classes in C.

https://www.tutorialspoint.com/function-pointer-in-c


Edited by Dawoodoz on

The core insight in the way Go does interfaces is that you pass around pairs of pointers like

struct interface { void *value; void *methods; };

instead of putting the methods pointer on the object. This means to do polymorphism you just need a way to figure out if there is an appropriate table of methods for the value type you have and you stuff it in there. In C... keep it simple, it's not going to be as ergonomic as go

Here's the email about Haskell that gets passed around as the mythic realization of the potential of this idea, if you're curious https://homepages.inf.ed.ac.uk/wadler/papers/class-letter/class-letter.txt

For a ray tracer, though, you might not need polymorphism at all.

Now, how about several spheres? While it is tempting to have an array of spheres,

Indeed it is, Peter, indeed it is

Thanks. This sounds interesting: how would you implement a ray tracer without polymorphism?


Replying to graeme (#26368)

Well, the thing polymorphism is giving you is a way to leave unanswered the question of how many primitives you support. The code doesn't have to know. But for a ray tracer you probably do know and can just partition primitives by their type and iterate over them knowing their type upfront. Just as Shirley wrote--have an array of spheres, have an array of planes, have an array of meshes... This is pretty common in performance oriented code. It keeps all the spheres together, keeps all the (binary) code for operating on spheres together without branching/dispatching on types all the time, and this keeps the cpu happy.

On the other hand after a certain point with a ray tracer you are mostly going to be dealing with meshes and hierarchies of meshes (AABBs and so on). So another answer is to have so few primitives you don't care, and focus on meshes and collections of meshes. I mean really you still have a kind of polymorphism here but the polymorphism is in the data you process. Generally this is a nice place to be, unless you specify your data in a turing complete format


Replying to escalioth (#26502)