繁体   English   中英

使用宏获取 Haxe function 参数类型的最佳方法是什么?

[英]What is the best way to get Haxe function parameter types using a macro?

我想使用宏获取 Haxe function 的参数类型并将它们转换为简写字符串形式,有点像 JNI/Java 方法签名,但没有返回类型。

这里的动机是提供对 function 参数类型的访问,而不必在运行时慢慢搜索运行时类型信息。 例如,假设您要构造一个图形小部件来调用带参数的 function。 您将需要每个 function 参数的类型来创建正确的旋转框、文本框和 select 框小部件,以调整将传递给 function 的值。

那么问题来了,如何用宏保存Haxe function参数类型呢?

这是一个适用于一些基本类型以及基于这些类型的任何抽象的宏。 它将函数参数类型映射为字符串。 例如,函数类型String->Float->Int->String->Void sfis String->Float->Int->String->Void映射到sfissfis Float->Float->Int sfis Float->Float->Int映射到ff等:

package;

import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Type;
import haxe.macro.ExprTools;

// Map some Haxe types to string ids
@:enum abstract TypeMapping(String) from (String) {
    var BOOL = "b";
    var FLOAT = "f";
    var INT = "i";
    var STRING = "s";
}

class Util
{
    public macro static function getParameterTypes(f:Expr):ExprOf<String> {
        var type:Type = Context.typeof(f);
        if (!Reflect.hasField(type, 'args')) {
            throw "Parameter has no field 'args'";
        }
        var t = type.getParameters()[0];

        var args:Array<Dynamic> = Reflect.field(type, 'args')[0];

        var signature:String = "";
        for (i in 0...args.length) {            
            switch(args[i].t) {
                case TAbstract(t, p):
                    var underlyingTypeName = Std.string(t.get().type.getParameters()[0]);
                    switch(underlyingTypeName) {
                        case "Bool":
                            signature += TypeMapping.BOOL;
                        case "Float":
                            signature += TypeMapping.FLOAT;
                        case "Int":
                            signature += TypeMapping.INT;
                        case "String":
                            signature += TypeMapping.STRING;
                        default:
                            throw "Unhandled abstract function parameter type: " + underlyingTypeName;
                    }
                case CString:
                    signature += TypeMapping.STRING;
                default:
                    throw "Unhandled function parameter type: " + args[i];
            }
        }
        return macro $v{signature};
    }
}

另一个问题是如何使它适用于所有类型,而不仅仅是您明确处理的类型。 为此,您可以使用每个函数参数的类型名称/类名称/路径填充字符串数组,然后返回它而不是单个字符串。 这是一种尝试,请注意,它尚不适用于函数参数(可能还有其他功能):

public macro static function getFullParameterTypes(f:Expr):ExprOf<Array<String>> {
    var type:Type = Context.typeof(f);
    if (!Reflect.hasField(type, 'args')) {
        throw "Parameter has no field 'args'";
    }
    var args:Array<Dynamic> = Reflect.field(type, 'args')[0];

    var pos = haxe.macro.Context.currentPos();
    var signature:Array<Expr> = [];

    for (i in 0...args.length) {
        var argType:Type = args[i].t;
        var s;
        switch(argType) {
            case TFun(t, r):
                s = EConst(CString("Function"));
                throw "Not working with function parameters yet";
            case _:
                s = EConst(CString(argType.getParameters()[0].toString()));
        }
        signature.push({expr: s, pos: pos});
    }
    return macro $a{signature};
}

一个更新的方法..

macro function deflate(fun:haxe.macro.Expr) {
    var type = haxe.macro.Context.typeof(fun);
    final paramNames = extractFunction(type);

    return macro $v{paramNames};
}
// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
    return switch type {
        case TFun(args, ret): {
                var paramNames:Array<Dynamic> = [];
                for (p in args) {
                    final pName = p.name;
                    paramNames.push(pName);
                }

                return paramNames;
            }
        case _: {throw "unable to extract function information";};
    }
}

像这样使用

using Macros;
function func(name:String, greeting:String){};
final args = fun.deflate(); 
trace(args) // output: [name, greeting]

您可能面临的一个问题是如何收集参数的默认值,请考虑下面的示例。

function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = fun.deflate(); 
trace(args) // output: [name, greeting]

现在让我们通过稍微修改代码来考虑默认参数值:

// Extract function parameter names
function extractFunction(type):Array<Dynamic> {
    return switch type {
        case TFun(args, ret): {
                var paramNames:Array<Dynamic> = [];
                for (p in args) {
                    final pName = p.name;
                    final v = {name: pName, value: null}; // <= anticipate a value
                    paramNames.push(v);
                }

                return paramNames;
            }
        case _: {throw "unable to extract function information";};
    }
}


macro function deflate(fun:haxe.macro.Expr) {
    var type = haxe.macro.Context.typeof(fun);
    final paramNames:Array<Dynamic> = extractFunction(type);
    // extract default param values 
    switch  fun.expr {
        case EFunction(f, m):{
            for(a in m.args){
                for(p in paramNames){
                    if(p.name == a.name){
                        if(a.value != null){
                            switch (a.value.expr){
                                case EConst(c):{
                                    switch(c){
                                        case CString(v, _):{
                                            p.value = v;
                                        }
                                        case CFloat(f): {
                                            p.value = Std.parseFloat(f);
                                        }
                                        case CInt(i):{
                                            p.value = Std.parseInt(i);
                                        }
                                        case _: throw "unsupported constant value for default parameter";
                                    }
                                }
                                case _:
                            }
                        }
                    }
                }
            }
        }
        case _:
    }
    return macro $v{paramNames};
}

所以我们现在可以像这样使用它

function func(name:String = "Josh", greeting:String = "Hello"){ return '$greeting $name'};
final args = Macros.deflate(func); 
trace(args) // output: [{name: 'name', value:'Josh', {name:'greeting', value:'Hello'}]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM