Определение необязательного аргумента extern для функции с обязательным обнуляемым аргументом int - PullRequest
1 голос
/ 04 ноября 2019

Предположим, у вас есть функция extern, которая принимает аргумент Null<Int>.
Этот аргумент требуется в целевом API, но вы хотите, чтобы по умолчанию он был равен 0 при вызове из Haxe. То есть

class Test {
    static function main() {
        Ext.func();
        Ext.func(1);
        Ext.func(null);
    }
}
extern class Ext { ... definition for func }

должно выдать

Ext.func(0);
Ext.func(1);
Ext.func(null);

соответственно.

Это странно проблематично, давайте пока рассмотрим мои соображения:

Очевидный подход

class Test {
    static function main() {
        Ext.func();
        Ext.func(0);
        Ext.func(null);
    }
}
extern class Ext {
    static function func(v:Null<Int> = 0):Void;
}

Производит

Ext.func(); // (note the omitted parameter)
Ext.func(0);
Ext.func(null);

и, следовательно, ваш сгенерированный код не компилируется.

Встроенная оболочка

class Test {
    static function main() {
        Ext.func();
        Ext.func(1);
        Ext.func(null);
    }
}
extern class Ext {
    static inline function func(v:Null<Int> = 0):Void {
        realFunc(v);
    }
    @:native("func") private static function realFunc(v:Null<Int>):Void;
}

Производит

Ext.func(0);
Ext.func(1);
Ext.func(0); // (note the null turning into 0)

и, следовательно, ваш код не работает должным образом.

Макросы

Как гласит старая поговорка , любое упущение в Haxe можетбыть исправлено с помощью достаточно сложного макроса. Это подтверждается еще раз, но по какой цене?

Test.hx:

class Test {
    static function main() {
        Ext.func();
        Ext.func(1);
        Ext.func(null);
    }
}

RealExt.hx:

@:native("Ext") extern class RealExt {
    static function func(v:Null<Int>):Void;
}

Ext.hx:

import haxe.macro.Expr;
class Ext {
    public static macro function func(args:Array<Expr>):Expr {
        if (args.length > 1) throw "Too many arguments!";
        var v = args[0];
        if (v == null) v = macro 0;
        return macro RealExt.func($v);
    }
}

Таким образом, мы не можем поместить макрос во внешний класс, поэтому нужен прокси-класс (если вы хотели это в служебном классе, я думаю, вы теперь получили избыточность ... если вы не хотите написатьмакрос, который перебирает поля класса и строит прокси-класс?), и вы не можете просто иметь необязательный аргумент? expr (или он превратится в EConst (CIdent (null)))), так что это небольшое чудовище. Но это работает, да

Ext.func(0);
Ext.func(1);
Ext.func(null);

Признавая свое поражение

Может быть, все не так просто, как нам хотелось бы.

class Test {
    static function main() {
        Ext.func();
        Ext.funcExt(1);
        Ext.funcExt(null);
    }
}
extern class Ext {
    static inline function func():Void {
        funcExt(0);
    }
    @:native("func") static function funcExt(v:Null<Int>):Void;
}

Очевидно, это работает,

Ext.func(0);
Ext.func(1);
Ext.func(null);

но выглядит не очень чисто.

Не признаю вашего поражения

Не знаю, лучше это или хуже

class Test {
    static function main() {
        Ext.func();
        Ext.func(1);
        Ext.funcWithNull();
    }
}
extern class Ext {
    static inline function func(v:Null<Int> = 0):Void {
        funcExt(v);
    }
    static inline function funcWithNull():Void {
        funcExt(null);
    }
    @:native("func") private static function funcExt(v:Null<Int>):Void;
}

Очевидно, что это работает, только если вы знаете, что будете вызывать его с помощью null, и также не работает, если существует более одного такого аргумента.

Использование магического значения для указания того, что выхотел ноль

Это звучит как просьба о неприятностях, может, давайте не будем


Возможно, есть лучший способ сделать это?

...