Yes there is: va_list.
> no way to express forwarding an arbitrary return value
Of course there is, and lots and lots of language runtimes implemented in C use those ways. Usually it boils down to having a base type called Object or Value and passing around pointers to that. In fact, from your example it looks like the "id" type is meant to play this role.
This is not syntax checked, but the code above would be something like:
Object *objc_msgSend(id self, SEL cmd, ...) {
fptr = ...lookup code...
va_list args;
va_start(cmd, args);
Object *result = fptr(self, cmd, args);
va_end(args);
return result;
}
Yes, this can be faster in assembly, but it's not true that there is no way to express this. (Unless I'm misunderstanding something.)