Using Haxe macro for conditional compilation, instead of #if #end

萝らか妹 提交于 2020-01-02 14:05:13

问题


Suppose we have a Local class with :

class Local {
    static inline public var logLevel:Int = 3;
}

And some functions :

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

We can achieve this using -D and some #if in the code.

However that implies modifying the hxml file all the time. Even if it's just for one value, this isn't ideal for me as all my configuration sits in my Local class.

And if we test the value with simple if (), the code gets bigger with all the if and strings, even if it's never used (because the logLevel is meant as a 'constant').

Is it possible to use macros to overcome those two problems?


回答1:


You can just make your logging functions inline. Compiler will inline them and check the conditions in compile time, removing branches which wouldn't have a chance to be executed.




回答2:


Edit:

@stroncium's answer is much easier if the use case is this simple: make everything inline and the compiler will do the optimisations, no macros required. See http://try.haxe.org/#C44Ec, and take a look at the compiled JS.


Assuming Local is a normal class (you write it by hand, you don't build it with a macro or anything else), then you can access Local.logLevel in a macro call.

Some code like this would work

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

Or the same thing, written a little more tersely, and for all your functions:

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



回答3:


It's poor style to make your source or config files so that they require editing every time you compile.

If you instead use an IDE like FlashDevelop, then the problem kind of goes away because you can pass in the parameters at compile time. Which is what an IDE is for, after all.

I'll use FD as an example here, but you can adapt this for whatever IDE you use.

FlashDevelop has two dropdowns at the top: Configuration and Target. For example, in my project, I have the usual two configurations (Debug, Release) and three targets (standalone, webalone, webinline). You could have targets like "log0", "log1" etc.

[Unfortunately, FlashDevelop currently has a bug where it doesn't store config names between sessions, so I often have to type in the one I want before compiling.]

In project properties -> pre-build commandline, I put:

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

You can specify multiple hxml files for a haxe build, so you can have one set called log1.hxml, log2.hxml, etc, which contain only that one line you keep changing.

You could leave it at that. It does everything you've asked for. You could even use the "target" field as a "loglevel" field, and just put a number in. Your compile commandline could then look something like:

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

Personally, for debugging, I needed a different debug environment (browser, flash player, etc) for each target, so I made the debugging action a batch file:

  • Project properties ->
  • Output ->
  • Test project ->
  • Run Custom Command ->
  • "D:\svn\src\haxe\debug.bat" $(TargetBuild) "$(OutputFile)"

That batch file contains:

@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

So in short: use the power of your IDE to do what you want from a simple dropdown each time you compile.



来源:https://stackoverflow.com/questions/25225149/using-haxe-macro-for-conditional-compilation-instead-of-if-end

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!