【问题标题】:Using Haxe macro for conditional compilation, instead of #if #end使用 Haxe 宏进行条件编译,而不是 #if #end
【发布时间】:2014-08-10 03:07:52
【问题描述】:

假设我们有一个本地类:

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 文件。即使它只是为了一个值,这对我来说并不理想,因为我的所有配置都位于我的 Local 类中。

如果我们用简单的 if () 来测试这个值,那么代码会随着所有的 if 和字符串变得更大,即使它从未使用过(因为 logLevel 的意思是“常量”)。

是否可以使用宏来解决这两个问题?

【问题讨论】:

  • 最好将配置细节与代码分开存储。宏函数可以读取这些文件,并更改编译代码的细节。

标签: macros haxe


【解决方案1】:

编辑:

如果用例如此简单,@stroncium 的答案会容易得多:使所有内容内联,编译器将进行优化,不需要宏。见http://try.haxe.org/#C44Ec,看看编译好的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;
        }
    }
}

或者同样的东西,写得更简洁一些,并且适用于你的所有功能:

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;
    }
}

【讨论】:

  • 如果您的用例如此简单,@stroncium 的答案比我的要好。不需要宏,只需将所有变量和函数内联,它就会全部清理干净。
【解决方案2】:

您可以使您的日志记录功能内联。编译器将内联它们并在编译时检查条件,删除没有机会执行的分支。

【讨论】:

  • 我检查了 Jason O'Neil 在此页面上发布的 try.haxe.org/#C44Ec,如果“常量”值不满足条件,则显示内联不会在 Javascript 中编译。如果所有平台(在我的例子中是 Neko 和 Swf)都能保证这种行为,那么我想这是解决我的问题的更好方法。
  • 据我所知,在编译器的这一部分没有错误的情况下,它得到了尽可能多的保证。我从来没有见过这不起作用,但如果你处于最前沿,那么各种事情有时会有点奇怪。
【解决方案3】:

制作源文件或配置文件的风格很糟糕,因此每次编译时都需要对其进行编辑。

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

我将在此处使用 FD 作为示例,但您可以根据您使用的任何 IDE 进行调整。

FlashDevelop 顶部有两个下拉菜单:配置和目标。例如,在我的项目中,我有通常的两个配置(Debug、Release)和三个目标(standalone、webalone、webinline)。您可以有“log0”、“log1”等目标。

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

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

haxe.exe $(BuildConfig).hxml $(TargetBuild).hxml $(ProjectName).hxml

您可以为 haxe 构建指定多个 hxml 文件,因此您可以有一组称为 log1.hxml、log2.hxml 等,其中仅包含您不断更改的那一行。

可以把它留在那里。它可以满足您的所有要求。您甚至可以将“目标”字段用作“日志级别”字段,然后输入一个数字。然后您的编译命令行可能类似于:

haxe.exe $(ProjectName).hxml -D loglevel=$(TargetBuild)

就个人而言,为了调试,我需要为每个目标使用不同的调试环境(浏览器、flash 播放器等),因此我将调试操作设为批处理文件:

  • 项目属性->
  • 输出->
  • 测试项目->
  • 运行自定义命令->
  • "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 的强大功能从一个简单的下拉列表中执行您想要的操作。

【讨论】:

  • 我当时的IDE是TextMate和控制台,但是如果我采用IDE我会考虑这个。
  • 顺便说一句,我不知道可以使用多个配置文件,这可以方便地将基本配置与开发/测试/生产细节分开,如您所示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
  • 2019-01-30
  • 2022-01-15
  • 1970-01-01
相关资源
最近更新 更多