haxe
haxe copied to clipboard
Generated .cpp files have conversion errors: "cannot convert from 'String' to 'Float'"
When building projects for Windows targets, Haxe code which concatenates arbitrary values into strings produces invalid .cpp output with errors:
Example .hx input:
var choices = new Array<String>();
choices.push("(" + choice + ")");
Resulting .cpp output:
HXLINE( 204) Float floatA = HX_("(",28,00,00,00);
HXDLIN( 204) Float floatB = ( (Float)(choice) );
HXDLIN( 204) _hx_tmp1 = (floatA + floatB);
Error:
./src/DialogTree.cpp(287): error C2440: 'initializing': cannot convert from 'String' to 'Float'
./src/DialogTree.cpp(287): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
This can be worked around by explicitly calling Std.string() on any non-string parameters. This is not required for other targets such as HTML5 or Flash.
var choices = new Array<String>();
choices.push("(" + choice + ")");
Here is a list of all of the dependency versions I am using.
Please post a complete example, in particular what choice is in that code.
The code in question is 500 lines long, but here are the pertinent lines of code for the data types.
...
import openfl.utils.Object;
...
var _tree:Array<Array<Object>> = new Array<Array<Object>>();
...
for (choice in _tree[_currentLine + 1])
...
What type is Object? I don't see where the Float comes from here, and the problem doesn't reproduce if I use Dynamic.
Sorry -- I've amended the code snippet to include the necessary import statement (openfl.utils.Object)
What Haxe version are you on?
Haxe 4.3.1.
I cannot reproduce this... Here's my attempt at reducing the example:
import haxe.Constraints.Function;
@:dox(hide) @:noCompletion typedef ObjectType = Dynamic;
@:transitive
@:callable
#if !haxe4
@:forward
#end
abstract Object(ObjectType) from ObjectType from Dynamic to Dynamic {
public inline function new() {
this = {};
}
public inline function hasOwnProperty(name:String):Bool {
return (this != null && Reflect.hasField(this, name));
}
@SuppressWarnings("checkstyle:FieldDocComment")
@:noCompletion @:dox(hide) public function iterator():Iterator<String> {
if (#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (this, Array)) {
var arr:Array<Dynamic> = cast this;
return arr.iterator();
} else {
var fields = Reflect.fields(this);
if (fields == null)
fields = [];
return fields.iterator();
}
}
@:to public inline function toString():String {
return (this == null ? null : Std.string(this));
}
@:op(a.b)
private inline function __fieldRead(name:String):Object {
return __get(name);
}
#if haxe4
@:op(a.b)
private inline function __fieldWrite(name:String, value:Object):Object {
return __set(name, value);
}
#end
@SuppressWarnings("checkstyle:FieldDocComment")
@:arrayAccess @:noCompletion @:dox(hide) public /*inline*/ function __get(key:String):Object {
if (this == null || key == null)
return null;
if (Reflect.hasField(this, key)) {
return Reflect.field(this, key);
}
return Reflect.getProperty(this, key);
}
@SuppressWarnings("checkstyle:FieldDocComment")
@:arrayAccess @:noCompletion @:dox(hide) public inline function __set(key:String, value:Object):Object {
if (this != null) {
Reflect.setProperty(this, key, value);
}
return value;
}
@SuppressWarnings("checkstyle:FieldDocComment")
@:arrayAccess @:noCompletion @:dox(hide) public function __getArray(index:Int):Object {
if (this == null)
return null;
var arr = cast(this, Array<Dynamic>);
return arr[index];
}
@SuppressWarnings("checkstyle:FieldDocComment")
@:arrayAccess @:noCompletion @:dox(hide) public function __setArray(index:Int, value:Object):Object {
if (this == null)
return value;
var arr = cast(this, Array<Dynamic>);
return arr[index] = value;
}
@:to private function toFunction():Function {
return cast this;
}
@:to private function toFloat():Float {
if (#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (this, Float)) {
return cast this;
} else {
return Math.NaN;
}
}
@:to private function toInt():Int {
return cast this;
}
@:to private function toBool():Bool {
if (#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (this, Bool)) {
return cast this;
} else if (#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (this, Float)) {
return this != 0;
} else {
return this != null;
}
}
}
function main() {
var _tree:Array<Array<Object>> = new Array<Array<Object>>();
for (choice in _tree[0]) {
var choices = new Array<String>();
choices.push("(" + choice + ")");
}
}
Please see if that reproduces it as well. Otherwise there must be some deeper problem.
I don't know how to run your example; I included it in my HaxeFlixel project but it was not interpreted or compiled to .cpp. But I suspect it would not reproduce the problem.
package;
import openfl.utils.Object;
class Example2
{
public static function asdf()
{
var tree:Array<Object> = ["foo"];
tree[0] = tree[0] + "bar";
}
}
Here is a simple example which reproduces the problem. If I replace openfl.utils.Object with Dynamic the error no longer occurs.
Here is a sample that has been stripped down further:
abstract Object(Dynamic) from Dynamic to Dynamic {
@:op(A + B) private static inline function __add(a:Object, b:Object):Dynamic {
if (a is String || b is String) {
return Std.string(a) + Std.string(b);
} else {
var floatA:Float = cast a;
var floatB:Float = cast b;
return floatA + floatB;
}
}
@:op(A + B) @:commutative private static inline function __addString(a:Object, b:String):String {
var stringA:String = Std.string(a);
return stringA + b;
}
}
class Main
{
public static function main()
{
var tree:Object = "foo";
trace(tree + "bar");
// trace("bar" + tree); // < -- this also causes the same error
}
}
The error occurs due to the inline, which means the String is cast directly to a Float instead of passing through Dynamic first.
I also wonder if the priority of operator overloads is correct here, because __add is taking priority over __addString. Putting __addString first results in it taking priority over the __add.