I have the concept that natives return something called a Bounce.
A Bounce is a superset of a Value*, and in C it's just a const void* (because there aren't any other polymorphic base classes that don't violate strict aliasing rules).
So in C:
typedef const void* RebolBounce;
That's dangerous in the type system, because you can return a pointer to any type and it will compile (when only a certain set of things the system can "sniff" and discern are actually legal.)
So the C++ build is more clever. It makes Bounce into a structure with specialized construction:
struct Bounce {
const void* p; // the single element in the struct
Bounce (Value* v) { ... } // final result from an API value
Bounce (Level* L) { ... } // continuation of a level
Bounce (const char* cp) { ... } // delegation to scanned source code
Bounce (nullptr_t) { ... } // return a ~null~ antiform
... ; and so on
};
Unfortunately, these two forms are not "ABI-compatible".
What that means is that if you compile the core with the struct definition of Bounce and an extension with the typedef... or if you compile the core with the typedef and the extension with the struct... these won't work together.
That has caused a rift in the definitions--where the "external" part of the system uses different expectations (the typedef) than what's internal to the core (the struct definition if it's a C++ build and it's a debug build).
There's No Way Around This, Unless...
The only real way to work around this would be to say that Bounce is always a single element struct, and if you want your code to compile as C your natives have to make a struct manually.
Returning Text Strings Would Become rebDelegate()
Instead of writing:
rebElide("append block data");
...
return "first block";
You'd could write:
rebElide("append block data");
...
return (Bounce){"first block"};
But that's awkward. This was always a shortcut for rebDelegate() passed a single string (rebDelegate is variadic and can do composition, so it's much more powerful):
rebElide("append block data");
...
return rebDelegate("first block");
And if you were committed to using C++, you could bypass this and just use the string as before.
Returning Values could be rebOut(v) or similar
So instead of writing:
Value* v = rebValue(...);
...
return v;
You'd could again write the painful:
Value* v = rebValue(...);
...
return (Bounce){v};
Or use something that did that like rebOut():
Value* v = rebValue(...);
...
return rebOut(v);
It could also be called something like rebResult(v), rebReturn(v) w/shorthand rebRet(v) etc. (I think what worries me a bit about "rebReturn(v)" is that suggests a hooked RETURN would be executed, like a synonym for rebDelegate("return @", v);
Once again, if you committed to a C++ build for your extension you could just say return v; as usual.
Worth It (Or Does It Lose The "Magic"?)
I'm torn. It really would make the mechanics better if the Bounce was always a struct. ![]()
I kind of hate the idea of writing return rebOut(v);. The effort expended to slice up the bit-space to differentiate between legal leading bytes for UTF-8 strings and the various internal entities feels like it's going to waste somewhat if you have to use a function that produces what looks like a different "type".
But I think I just can't sacrifice the cool C trick.