csharp unable to generate correct code for extern accepting a callback #5907

Open
binki opened this Issue Dec 25, 2016 · 3 comments

Projects

None yet

3 participants

@binki
Contributor
binki commented Dec 25, 2016

Given this csharp:

using System;

namespace CallbackConsumer
{
    public static class Class1
    {
        public static string consume(string x, Func<string, string> f)
        {
            return f(x);
        }
    }
}

and this extern:

@:expose
@:native('CallbackConsumer.Class1')
extern class Class1 {
    public static function consume(x:String, f:(String->String)):String;
}

and this client haxe:

class Run {
    static function main() {
        trace(Class1.consume('asdf', function (s) {
            return 'x${s}';
        }));
    }
}

yields:

C:\Users\ohnob\OneDrive\Documents\Visual Studio 2017\Projects\CallbackConsumer>haxe -main Run -cp CallbackConsumer/externs -cs bin/Run.cs -net-lib CallbackConsumer/bin/Debug/CallbackConsumer.dll
haxelib run hxcs hxcs_build.txt --haxe-version 3400 --feature-level 1
src\Run.cs(27,59): error CS1502: The best overloaded method match for 'CallbackConsumer.Class1.consume(string, System.Func<string,string>)' has some invalid arguments
src\Run.cs(27,109): error CS1503: Argument 2: cannot convert from 'Run_main_3__Fun' to 'System.Func<string,string>'
Compilation error
Native compilation failed
Error: Build failed

This is because Haxe generates some Function class instead of trying to actually pass the delegate around directly. The generated Function class does not inherit from Delegate because you can’t do that in csharp. In csharp code, you would just let the compiler automatically generate a delegate for you instead. For example, if the delegate needs to access state, you can just write it as an instance method of a class and pass classInstance.MethodName. If you want to pack RTTI, you can access the object the delegate is attached to via Delegate.Target, check if it is Haxe-generated object or not, and go from there.

@nadako
Member
nadako commented Dec 25, 2016

Haxe/C# function objects are not .NET delegates, if you want to hand-write externs for delegates, you need to use e.g. cs.system.Func_1<String,String>. Haxe will support passing callbacks for those types directly though.

@binki
Contributor
binki commented Dec 25, 2016

As indirectly suggested (to use -net-libs’s automatic type parsing), if I update my CallbackConsumer/externs/Class1.hx to be:

#if cs
typedef Class1 = callbackconsumer.Class1;
#else
extern class Class1 {
    public static function consume(x:String, f:(String->String)):String;
}
#end

I still get a failure—from haxe this time:

C:\Users\ohnob\OneDrive\Documents\Visual Studio 2017\Projects\CallbackConsumer>haxe -main Run -cp CallbackConsumer/externs -cs bin/Run.cs -net-lib CallbackConsumer/bin/Debug/CallbackConsumer.dll
Type not found : cs.system.Func_2
Run.hx:1: lines 1-7 : Defined in this class
@Simn Simn modified the milestone: 4.0 Jan 9, 2017
@binki
Contributor
binki commented Jan 11, 2017 edited

EDIT: I need to actually gather my thoughts and ponder this longer, sorry for the bugspam. Also, I accidentally hit ctrl+enter xD.

This is one case where Haxe seems to treat externs inconsistently with itself. If I build a .net library with a kept type for the code

class CallbackConsumer {
    public static function consume(value:String, f:String->(String->Void)->Void):String return 'hi';
}

I get C# like this:

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