Skip to content

Call a parent toString that takes an optional argument. - #1403

Open
Tutez64 wants to merge 4 commits into
HaxeFoundation:masterfrom
Tutez64:bugfix/scriptable-tostring
Open

Tutez64 wants to merge 4 commits into
HaxeFoundation:masterfrom
Tutez64:bugfix/scriptable-tostring

Conversation

@Tutez64

@Tutez64 Tutez64 commented Sep 24, 2026

Copy link
Copy Markdown

HX_DEFINE_SCRIPTABLE always calls __superString::toString() with no arguments. A Haxe toString(indent = 0) is one C++ parameter, so that call does not compile.

The zero-argument form is used when it exists. Otherwise null is passed for the default.

The cppia host build compiles OptionalToString, whose toString takes an optional argument, and ZeroArgToString, whose toString does not. Without this change that host build does not compile.

Tutez64 and others added 2 commits September 24, 2026 23:34
HX_DEFINE_SCRIPTABLE always calls __superString::toString() with no
arguments. A Haxe toString(indent = 0) is one C++ parameter, so that call
does not compile.

The zero-argument form is used when it exists. Otherwise null is passed
for the default.

Co-authored-by: Cursor <cursoragent@cursor.com>
The cppia host build is what failed: toString(indent = 0) is one C++
parameter, and the scriptable wrapper called it with none. A zero-argument
toString is compiled in the same file so that form stays valid.

Co-authored-by: Cursor <cursoragent@cursor.com>
@tobil4sk

tobil4sk commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Seems like this is more of a specific hack to fix a program with that toString signature, but not a good general solution if the toString signature is different

A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Tutez64

Tutez64 commented Oct 3, 2026

Copy link
Copy Markdown
Author

You're right. Passing null only covered a single optional parameter. Looking closer, the failing case is a class that inherits a toString with parameters: that C++ overload hides hx::Object::toString(), and gencpp's __superString is then the class itself. Natively, Std.string on such an object reaches hx::Object::toString() whatever the signature, so in 95c73b3 the wrapper now falls back on exactly that when the parent has no zero-argument toString. The cppia tests now build host classes that inherit toString(indent = 0) and toString(indent, prefix), and check at run time that script subclasses print like native ones.

@tobil4sk

tobil4sk commented Oct 9, 2026

Copy link
Copy Markdown
Member

This change still doesn't make sense, there is a rogue int argument that only makes sense in the context of the toString(indent = 0) example. It seems claude is still cheating by hardcoding a fix that only works for the specific test?

I think the toString(indent, prefix) case should be handled by Haxe: https://github.com/HaxeFoundation/haxe/blob/995a7d1fe92bfded79798598b8ad243cae05dda6/src/generators/cpp/gen/cppGenClassImplementation.ml#L994

Could you clarify what the compiler error actually is? Is it that __superString::toString() tries to be a call to toString(indent = 0) instead of resolving to hx::Object::toString()?

dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
dimensionscape pushed a commit to openfl/libhxcpp that referenced this pull request Oct 15, 2026
A Haxe toString with parameters compiles to a C++ overload that hides
hx::Object::toString() without overriding it, whatever the parameters.
Passing null for them only worked for a single optional one. Native code
calling toString() on such an object reaches hx::Object::toString(), so
the scriptable wrapper now calls that one too.

The cppia tests now build host classes that inherit a toString with one
optional parameter and with two required ones, and check at run time
that script classes extending them print like native ones.

HaxeFoundation/hxcpp#1403

Fixes #32
@Tutez64

Tutez64 commented Oct 16, 2026

Copy link
Copy Markdown
Author

Yes, that's it. With current Haxe development and hxcpp master, a host built with -D scriptable from:

class OptionalToString {
        public function new() {}
        public function toString(indent:Int = 0):String return "indent" + indent;
}
class OptionalToStringChild extends OptionalToString {}

fails with:

include/hx/Scriptable.h:238:42: error: no matching function for call to 'OptionalToStringChild_obj__scriptable::toString()'
include/OptionalToString.h:58:34: note: candidate: 'virtual String OptionalToString_obj::toString(Dynamic)'

has_funky_toString only looks at the class's own fields, so for the child __superString is the child itself, where the inherited toString(Dynamic) hides hx::Object::toString(). A class that overrides toString(indent = 0) fails the same way: its __superString is ::super, the parent that declares it.

The int isn't related to indent: it only ranks the two overloads. 0 matches int better than ..., so the first one is picked whenever T::toString() compiles, and SFINAE drops it otherwise. It should have had a comment.

I agree the generator is the better place, so I opened HaxeFoundation/haxe#13066: it walks up the hierarchy past the nearest funky toString. This hxcpp change would then only matter for Haxe versions that won't get that fix, like 4.3.x.

@tobil4sk

Copy link
Copy Markdown
Member

The int isn't related to indent: it only ranks the two overloads. 0 matches int better than ..., so the first one is picked whenever T::toString() compiles, and SFINAE drops it otherwise. It should have had a comment.

Thanks for the explanation, that makes more sense. Though it seems a bit hacky and it would be nice to avoid the dummy parameter.

If it's just a gap in what Haxe's generator is already doing then it does make sense to handle it there. It should also be possible to backport that fix to haxe 4.3.x so we can decide whether to add this workaround in hxcpp or to just rely on the haxe side patch.

ScriptableHasToString says whether `T::toString()` compiles, and the
wrapper dispatches on it with std::true_type / std::false_type, as
Boxed and Invoker already do, instead of ranking an `int` overload
over a `...` one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Tutez64

Tutez64 commented Oct 16, 2026

Copy link
Copy Markdown
Author

I replaced the dummy parameter in 4e1b2b7: a ScriptableHasToString<T> trait says whether T::toString() compiles, and the wrapper dispatches on std::true_type / std::false_type, like Boxed and Invoker already do. The cppia tests still pass with Haxe 4.3.7, with and without -jit, including ZeroArgToString, which goes through the T::toString() path.

About relying on the Haxe side: 4.3.7 came out in May 2025, 4.3_bugfix hasn't had a commit since. For now this is the only fix that reaches people. I don't plan to backport HaxeFoundation/haxe#13066 myself, but if it does land in a 4.3.x release, I'm fine with dropping this workaround then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants