There is another variation on structure embedding I have used in the past, which is to have the subtypes' data appended directly to the structure containing the supertype, with appropriate macros / functions to map from one offset to the other and to do appropriate casting. This gives you the advantages of structure-embedding subtyping without costing a dereference or an extra allocation, but has disadvantages, notably that you have to ensure correct alignmewnt of the trailing subtype field (easy enough) and that sizeof() no longer works. So this is only really usable when the datatype is itself hidden and only used within one translation unit, such that it is the only thing that can take the sizeof this structure, and can add an appropriate size for the subtype in use whenever a sizeof is needed.
Combine this with an ADT pointer and you have something pretty much identical in representation to C++ classes. The code is... a good bit hairier than C++ classes though.