Macros 使用Haxe宏进行条件编译,而不是#if#end

Macros 使用Haxe宏进行条件编译,而不是#if#end,macros,haxe,Macros,Haxe,假设我们有一个本地类: class Local { static inline public var logLevel:Int = 3; } 以及一些功能: Tool.debug(s:String) // compiled if logLevel >= 0 Tool.moreinfo(s:String)// compiled if logLevel >= 1 Tool.info(s:String) // compiled if logLevel >= 2 T

假设我们有一个本地类:

class Local {
    static inline public var logLevel:Int = 3;
}
以及一些功能:

Tool.debug(s:String)   // compiled if logLevel >= 0
Tool.moreinfo(s:String)// compiled if logLevel >= 1
Tool.info(s:String)    // compiled if logLevel >= 2
Tool.trace(s:String)   // compiled if logLevel >= 3
Tool.warn(s:String)    // compiled if logLevel >= 4
Tool.err(s:String)     // compiled if logLevel >= 5
我们可以在代码中使用-D和一些#if来实现这一点

但是,这意味着始终修改hxml文件。即使只是一个值,这对我来说也不理想,因为我的所有配置都位于本地类中

如果我们使用简单的if()测试该值,那么即使从未使用该值(因为logLevel是一个“常量”),所有if和字符串的代码也会变大

是否可以使用宏来克服这两个问题?

编辑:

@如果用例是这样简单的话,stroncium的答案就容易多了:将所有内容内联,编译器将进行优化,而不需要宏。请参阅,并查看编译后的JS


假设
Local
是一个普通类(您手工编写,不使用宏或其他任何东西构建),那么您可以在宏调用中访问
Local.logLevel

像这样的一些代码可以工作

class Tool {
    public static macro function debug( s:ExprOf<String> ):Expr {
        if ( Local.logLevel>=0 ) {
            // Insert a trace statement into our code. 
            // You could insert any other kind of statement or { block; of; statements; } also.
            return macro trace( $s );
        }
        else {
            // You need to return an expression of some kind.
            // This is the equivalent of writing the line "null;" - it does nothing whatsoever.
            return macro null;
        }
    }
}
类工具{
公共静态宏函数调试(s:ExprOf):Expr{
如果(Local.logLevel>=0){
//在代码中插入跟踪语句。
//您也可以插入任何其他类型的语句或{block;of;statements;}。
返回宏跟踪($s);
}
否则{
//您需要返回某种表达式。
//这相当于写一行“null;”——它什么都不做。
返回宏null;
}
}
}
或者同样的东西,写得更简洁一点,用于所有函数:

class Tool {
    public static macro function debug( s:ExprOf<String> ):Expr {
        return
            if ( Local.logLevel>=0 ) macro trace( "debug: " + $s );
            else return macro null;
    }
    public static macro function moreInfo( s:ExprOf<String> ):Expr {
        return
            if ( Local.logLevel>=1 ) macro trace( "moreInfo: " + $s );
            else return macro null;
    }
    public static macro function info( s:ExprOf<String> ):Expr {
        return
            if ( Local.logLevel>=2 ) macro trace( "info: " + $s );
            else return macro null;
    }
    public static macro function trace( s:ExprOf<String> ):Expr {
        return
            if ( Local.logLevel>=3 ) macro trace( "trace: " + $s );
            else return macro null;
    }
    public static macro function warn( s:ExprOf<String> ):Expr {
        return
            if ( Local.logLevel>=4 ) macro trace( "warn: " + $s );
            else return macro null;
    }
    public static macro function err( s:ExprOf<String> ):Expr {
        return
            if ( Local.logLevel>=5 ) macro trace( "err: " + $s );
            else return macro null;
    }
}
类工具{
公共静态宏函数调试(s:ExprOf):Expr{
返回
如果(Local.logLevel>=0)宏跟踪(“调试:+$s”);
否则返回宏null;
}
公共静态宏函数moreInfo(s:ExprOf):Expr{
返回
如果(Local.logLevel>=1)宏跟踪(“moreInfo:+$s”);
否则返回宏null;
}
公共静态宏函数信息(s:ExprOf):Expr{
返回
如果(Local.logLevel>=2)宏跟踪(“信息:+$s”);
否则返回宏null;
}
公共静态宏函数跟踪(s:ExprOf):Expr{
返回
如果(Local.logLevel>=3)宏跟踪(“跟踪:+$s”);
否则返回宏null;
}
公共静态宏功能警告(s:ExprOf):Expr{
返回
如果(Local.logLevel>=4)宏跟踪(“警告:+$s”);
否则返回宏null;
}
公共静态宏函数错误(s:ExprOf):Expr{
返回
如果(Local.logLevel>=5)宏跟踪(“err:+$s”);
否则返回宏null;
}
}

您只需将日志函数内联即可。编译器将内联它们并在编译时检查条件,删除没有机会执行的分支。

使源文件或配置文件在每次编译时都需要编辑,这是一种糟糕的风格

如果您改用类似FlashDevelop的IDE,那么问题就会消失,因为您可以在编译时传入参数。这就是IDE的用途

这里我将使用FD作为示例,但您可以将其应用于您使用的任何IDE

FlashDevelop在顶部有两个下拉列表:配置和目标。例如,在我的项目中,我通常有两个配置(调试、发布)和三个目标(独立、WebOnline和webinline)。你可以有“log0”、“log1”等目标

[不幸的是,FlashDevelop目前有一个bug,它没有在会话之间存储配置名称,因此我通常必须在编译之前键入我想要的配置名称。]

在项目属性->预构建命令行中,我将:

haxe.exe $(BuildConfig).hxml $(TargetBuild).hxml $(ProjectName).hxml
您可以为一个haxe构建指定多个hxml文件,因此可以有一个名为log1.hxml、log2.hxml等的集合,其中只包含您不断更改的那一行

你可以就此罢休。它能满足你的所有要求。您甚至可以将“target”字段用作“loglevel”字段,只需在其中输入一个数字。然后,编译命令行可能会如下所示:

haxe.exe $(ProjectName).hxml -D loglevel=$(TargetBuild)
就个人而言,为了调试,我需要为每个目标设置不同的调试环境(浏览器、flash player等),因此我将调试操作设置为批处理文件:

  • 项目属性->
  • 输出->
  • 测试项目->
  • 运行自定义命令->
  • “D:\svn\src\haxe\debug.bat“$(TargetBuild)”$(OutputFile)”
该批处理文件包含:

@echo off
goto %1%
goto end

:webinline 
start "" "D:\svn\src\haxe\bin\index-flashvar.html"
goto end

:webalone 
start "" "D:\svn\src\haxe\bin\index.html"
goto end

:standalone 
start "" "D:\Program Files (x86)\FlashDevelop\Tools\flexlibs\runtimes\player\11.9\win\FlashPlayerDebugger.exe" %2%
goto end

:end

因此,简而言之:使用IDE的强大功能,在每次编译时从一个简单的下拉列表中执行您想要的操作。

通常最好将配置详细信息存储在代码之外。宏函数可以读取这些文件,并更改编译代码的详细信息。@stroncium的答案比我的好,如果您的用例如此简单的话。不需要宏,只需把所有的变量和函数内联起来,它就会清理干净。我的IDE是TeMead和当时的控制台,但是如果我采用IDE,我会考虑这一点。BTW我不知道有可能使用多个配置文件,这可以方便地将基本配置与开发/测试/生产细节分开,如您所示。我检查了Jason O'Neil在本页上发布的内容,它显示如果“常量”值不满足条件,则内联线不会用Javascript编译。如果所有平台都能保证这种行为(在我的例子中是Neko和Swf),那么我想这是解决我的问题的更好的方法。据我所知,它是有保证的,就像它保证编译器的这一部分没有bug一样。我从来没见过这种方法不起作用,但如果你处于最前沿,各种各样的方法有时会有点奇怪。