Ah I get what you mean now. Yeah that would totally work. Having a custom tracing function per type lets the host language (C in this case) also store its own types in the GC Heap if so desired quite easily though, since all the programmer has to do is call gc_move(...) once per struct field in that function.
Writing out a typeinfo struct of some sort by hand is not as simple.
Maybe a good middle ground would be to register the tracing function separately with the GC Heap, which would return an ID for later use with gc_alloc.
I could also make the size just be the return value of the same function. The question is then what's worse, an extra 8 bytes in the header of every allocation, or the time spent in the function calculating the size. Since the function has to be called anyway, I'm guessing there's no point in storing it.
That would mean even the ID is not necessary, since an 8byte header is about as good as you can get due to padding.