问题
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