haxe icon indicating copy to clipboard operation
haxe copied to clipboard

Generated .cpp files have conversion errors: "cannot convert from 'String' to 'Float'"

Open Poobslag opened this issue 2 years ago • 10 comments

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

Poobslag avatar May 23 '23 09:05 Poobslag

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 + ")");

image

Here is a list of all of the dependency versions I am using.

Poobslag avatar May 23 '23 09:05 Poobslag

Please post a complete example, in particular what choice is in that code.

Simn avatar May 23 '23 09:05 Simn

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])
...

Poobslag avatar May 23 '23 09:05 Poobslag

What type is Object? I don't see where the Float comes from here, and the problem doesn't reproduce if I use Dynamic.

Simn avatar May 23 '23 09:05 Simn

Sorry -- I've amended the code snippet to include the necessary import statement (openfl.utils.Object)

Poobslag avatar May 23 '23 09:05 Poobslag

What Haxe version are you on?

Simn avatar May 23 '23 09:05 Simn

Haxe 4.3.1.

Poobslag avatar May 23 '23 09:05 Poobslag

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.

Simn avatar May 23 '23 09:05 Simn

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.

Poobslag avatar May 23 '23 10:05 Poobslag

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.

tobil4sk avatar Apr 11 '25 14:04 tobil4sk