Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion include/hx/Scriptable.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,26 @@ void __Visit(HX_VISIT_PARAMS) HXCPP_OVERRIDE { super::__Visit(HX_VISIT_ARG); ::h
#endif


/*
* A Haxe `toString` with parameters, whatever they are, compiles to a C++ overload that
* hides `hx::Object::toString()` without overriding it. In a class that inherits it,
* `__superString` is that class, so `__superString::toString()` does not compile.
* Native code calling `toString()` on such an object reaches `hx::Object::toString()`,
* so the scriptable wrapper calls that one in this case.
*/
template<typename T>
inline auto _hx_scriptable_super_toString(T *inSelf, int) -> decltype(inSelf->T::toString(), ::String())
{
return inSelf->T::toString();
}

template<typename T>
inline ::String _hx_scriptable_super_toString(T *inSelf, ...)
{
return inSelf->::hx::Object::toString();
}


#define HX_DEFINE_SCRIPTABLE(ARG_LIST) \
inline void *operator new( size_t inSize, int inExtraDataSize ) \
{ \
Expand All @@ -235,7 +255,7 @@ void __Visit(HX_VISIT_PARAMS) HXCPP_OVERRIDE { super::__Visit(HX_VISIT_ARG); ::h
void ** __GetScriptVTable() HXCPP_OVERRIDE { return __scriptVTable; } \
::String toString() HXCPP_OVERRIDE { if (__scriptVTable[0] ) \
{ ::hx::CppiaCtx *ctx = ::hx::CppiaCtx::getCurrent(); ::hx::AutoStack a(ctx); ctx->pushObject(this); return ctx->runString(__scriptVTable[0]); } \
else return __superString::toString(); } \
else return _hx_scriptable_super_toString<__superString>(static_cast<__superString *>(this), 0); } \
::String __ToString() const HXCPP_OVERRIDE { return ::hx::ScriptableToString(__scriptVTable[-1]); } \
::hx::Class __GetClass() const HXCPP_OVERRIDE { return ::hx::ScriptableGetClass(__scriptVTable[-1]); } \
int __GetType() const HXCPP_OVERRIDE { return ::hx::ScriptableGetType(__scriptVTable[-1]); } \
Expand Down
7 changes: 7 additions & 0 deletions test/cppia/Client.hx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ class ClientFoo implements IFoo {
}
}

// None overrides toString: the scriptable wrapper falls back on the host's.
class ClientOptionalToString extends OptionalToString.OptionalToStringChild {}

class ClientRequiredToString extends OptionalToString.RequiredToStringChild {}

class ClientZeroArgToString extends OptionalToString.ZeroArgToString {}

class Client
{
public static var clientBool0 = true;
Expand Down
32 changes: 32 additions & 0 deletions test/cppia/OptionalToString.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Scriptable hosts whose `toString` takes parameters, which hide `hx::Object::toString()`
* in C++, and one whose `toString` takes none. The children inherit a hiding `toString`,
* so the scriptable wrapper generated for them cannot call `toString()` on its parent.
*/
class OptionalToString {
public function new() {}

public function toString(indent:Int = 0):String {
return "indent" + indent;
}
}

class RequiredToString {
public function new() {}

public function toString(indent:Int, prefix:String):String {
return prefix + indent;
}
}

class ZeroArgToString {
public function new() {}

public function toString():String {
return "zero";
}
}

class OptionalToStringChild extends OptionalToString {}

class RequiredToStringChild extends RequiredToString {}
9 changes: 9 additions & 0 deletions test/cppia/cases/TestCommon.hx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ class TestCommon extends Test {
Assert.equals(2, Common.callbackSet, 'Bad cppia closure');
}

@:depends(testStatus)
function testHostToStringWithParameters() {
// As in native code, a toString with parameters is not the one Std.string calls.
Assert.equals('OptionalToStringChild', Std.string(new OptionalToString.OptionalToStringChild()));
Assert.equals('ClientOptionalToString', Std.string(Type.createInstance(Type.resolveClass('ClientOptionalToString'), [])));
Assert.equals('ClientRequiredToString', Std.string(Type.createInstance(Type.resolveClass('ClientRequiredToString'), [])));
Assert.equals('zero', Std.string(Type.createInstance(Type.resolveClass('ClientZeroArgToString'), [])));
}

@:depends(testStatus)
function testInterfaceCalling() {
final obj : IFoo = Type.createInstance(Type.resolveClass('ClientFoo'), []);
Expand Down
1 change: 1 addition & 0 deletions test/cppia/compile-host.hxml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-m CppiaHost
HostExtendedRoot
OptionalToString
-D scriptable
-D dll_export=host_classes.info
-L utest
Expand Down