簡體   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