The brief I've given myself is a bit contradictory in that I'm eschewing the use of virtual functions (as they make linking C to C++ more complicated) but, for now I'm still allowing boost as a dependency.
It turns out that Boost is highly useful for implementing polymorphic objects, since it contains variant - which can be used instead of the usual tagged unions.
To implement the basic Lisp Object I've created a template that implements the properties of the lisp object (copyable, assignable, and freely convertible to a base C type) and created a boost variant of all the different specialisations of this template which is the base class of our object.
This template should save writing boatloads of boilerplate code when contrasted with C and it should be much more easy to plug in new types. This has to be a win over C. The other big advantage (as noted before when I wrote the simpler Lisp) is that shared_ptr gives reference counted garbage collection more - or - less for free.
As usual, here's the code.
As usual, here's the code.