Incompatibility with Object.ToString()
Describe the bug
I can't move a class with a method :ToString() from XBase++ to X#
To Reproduce .prg
procedure Main()
local o
o := Example():new(5)
? o:ToString()
return
class Example
exported:
inline method Init(b)
::b := b
return self
inline method ToString()
return ntoc(::b)
var b
endclass
Expected behavior (xBase++ exe) Output
5
Actual behavior (X# compiler)
error XS9042: Override of virtual method 'ToString' in child class has CLIPPER calling convention but overridden method in parent class is STRICT.
error XS0508: 'Example.ToString(params usual[])': return type must be 'string' to match overridden member 'object.ToString()'
Additional context X# Compiler version 2.16.0.5 (public) -dialect:xBase++ -xpp1 -lb -memvar -vo1 -vo3 -vo5 -vo10 -vo15 -vo16
In .Net all classes do have a ToString() (and a couple more) method, as they are all derived from System.Objects, so this is something that can't be changed.
I think the solution is easy, specify the NEW modifier in the method declaration, to make your ToString() different than the standard ToString():
inline new method ToString()
An even better solution is to strongly type the method and give it the same signature with the standard method as in
METHOD ToString() AS STRING STRICT
(need to also specify OVERRIDE METHOD , if /vo3 is not enabled)
but I think you will not prefer this one, as it involves more changes in the code.
The modifier new only works if I add a type declaration to the variable o. This option does not suit me, since the type declaration will have to be added at all places of use.
procedure Main()
local o as Example
o := Example():new(5)
? o:ToString()
wait
return
class Example
exported:
inline method Init(b)
::b := b
return self
inline new method ToString()
return str(::b)
var b
endclass
When I rewrote the method signature like this
inline method ToString() as string strict
return str(::b)
I got a compiler error
error XS9002: Parser: unexpected input 'strict'
That's strange, the syntax with "strict" seems to compile fine here. Maybe there was some other syntax error in the code and the parser got confused?
But about the version with NEW, you are right, I think that's a general (not related only to the XBase++ dialect) bug in the runtime, when there exist both a CLIPPER and a STRICT method in the class hierarchy, then a late bound call to that method invokes always the parent one (no matter if it's the CLIPPER or STRICT one), instead of the child one.
Robert, do you agree this is a problem? Following sample code always prints "foo parent" and "bar parent", instead of calling the child ones:
CLASS Parent
METHOD Foo() AS VOID STRICT
? "foo parent"
METHOD Bar() CLIPPER
? "bar parent"
RETURN NIL
END CLASS
CLASS Child INHERIT Parent
NEW METHOD Foo() CLIPPER
? "foo child"
RETURN NIL
NEW METHOD Bar() AS VOID STRICT
? "bar parent"
END CLASS
FUNCTION Start( ) AS VOID
LOCAL u := Child{}
u:Foo()
u:Bar()
Chris, The child method in your example also displays "bar parent". So yes, that makes sense...
Robert
The description of the method syntax doesn't mention the identifier calling convention anywhere. Can I send you a sln-project? https://www.xsharp.eu/help/command_xpp_classmembers.html https://github.com/X-Sharp/XSharpPublic/issues/1286#issuecomment-1609047415
The calling convention was recently added in this commit https://github.com/X-Sharp/XSharpDev/commit/fb755e1dce948dcf6a4653d17f94f8ef2e9af6c2 to the xpp inline method, so that explains why the STRICT clause works for Chris and not for you. This commit also adds support for Type parameters to XPP methods.
Robert,
Oops, sorry. In the corrected code of the sample below, it prints
foo parent strict bar child strict
so it always calls the STRICT version of the method, no matter if it's in the child or parent class.
CLASS Parent
METHOD Foo() AS VOID STRICT
? "foo parent strict"
METHOD Bar() CLIPPER
? "bar parent clipper"
RETURN NIL
END CLASS
CLASS Child INHERIT Parent
NEW METHOD Foo() CLIPPER
? "foo child clipper"
RETURN NIL
NEW METHOD Bar() AS VOID STRICT
? "bar child strict"
END CLASS
FUNCTION Start( ) AS VOID
LOCAL u := Child{}
u:Foo()
u:Bar()
Did I understand correctly that the commit has already been made and I can update the compiler using this link? https://www.xsharp.eu/itm-downloads/download?path=installers%252FXSharpSetupPublic2.16.0.5.zip
The calling convention was recently added in this commit X-Sharp/XSharpDev@fb755e1 to the xpp inline method, so that explains why the STRICT clause works for Chris and not for you. This commit also adds support for Type parameters to XPP methods.
I am sorry but that fix is not included in the 2.16.0.5 build. That build is from end of may. The fix was done on June 1.
When is the next build coming out?
We plan a subscribers build in the coming weeks. Probably an internal build this week and a beta next week. We have not scheduled a date for the public build yet.