HXCPP_CXX_STANDARD Define
This has been on my list for a while, but with the C++17 define merge which recently went through it seems like a good time to bring it up.
Instead of having an ever growing list of defines for all the C++ versions I think having a define allowing you to specify the version would be better. E.g. -D HXCPP_CXX_STANDARD=14 to use C++14. One downside to this is that hxcpp xml only allows || in its if and unless tags, might need to add general comparisons to make it really useful.
<section if="${HXCPP_CXX_STANDARD} >= 17">
</section>
I played around with some of this months ago and since there's now a PR to add another version define I thought I'd better post some of my ideas!
Instead of adding more operators to hxcpps if / unless interpreter I thought it might be nice to re-use hscripts parser and write a custom interpreter which supports a small subset of operators, retains the existing behaviour of non existing defines returning false, and evaluates to a boolean. There could be a new eval xml tag to write these expressions in. e.g.
<files eval="HXCPP_CXX_STANDARD >= 17 && (HXCPP_M64 || EXTRA_DEFINE)">
</files>
class HxcppInterp extends Interp {
override function execute(expr:Expr):Dynamic {
return switch expr {
case EIdent('true'): true;
case EIdent('false'): false;
case EUnop('!', true, EIdent(d)):
valueOrDefined(d) == false;
case EUnop(_, _, _):
super.execute(expr);
case EBinop('||', EIdent(d1), EIdent(d2)):
valueOrDefined(d1) || valueOrDefined(d2);
case EBinop('||', EIdent(d), e2):
valueOrDefined(d) || execute(e2);
case EBinop('||', e1, EIdent(d)):
execute(e1) || valueOrDefined(d);
case EBinop('||', e1, e2):
execute(e1) || execute(e2);
case EBinop('&&', EIdent(d1), EIdent(d2)):
valueOrDefined(d1) && valueOrDefined(d2);
case EBinop('&&', EIdent(d), e2):
valueOrDefined(d) && execute(e2);
case EBinop('&&', e1, EIdent(d)):
execute(e1) && valueOrDefined(d);
case EBinop('&&', e1, e2):
execute(e1) && execute(e2);
case EBinop(_, _, _):
super.execute(expr);
case EParent(e):
execute(e);
case _:
false;
}
}
function valueOrDefined(v:String) {
return switch variables.get(v) {
case null:
false;
case value:
if (value is Bool) {
return cast value;
} else {
return true;
}
}
}
}
function main() {
final script = "HXCPP_CXX_STANDARD >= 17 && (HXCPP_M64 || EXTRA_DEFINE)";
final parser = new Parser();
final program = parser.parseString(script);
final interp = new HxcppInterp();
interp.variables.set('HXCPP_CXX_STANDARD', 17);
interp.variables.set('HXCPP_M64', true);
trace(interp.execute(program));
}
The one slight annoying thing with this is that you can't just put & in xml, you need to escape it. This is probably why the exisiting if / unless evaluator treats spaces as &&!
After this I realised that often you would want a "if / else" type expression and having to copy and paste the eval with a ! in front is both a pain and error prone if you forget to update one of them. I thought about adding if and switch xml tags but realised thats madness.
What seemed like a much more sensible idea was to add even more hscript support... My idea was a script tag that you could then programatically modify the build environment through a hxcpp hscript api. e.g.
<script>
Compiler.flag('foo');
Compiler.flag('-std=c++17', FlagType.Cpp);
</script>
<script files="SomeFile.hx"/>
I can't quite decide if this is brilliant or horrific. @tobil4sk might be interested in some of this, maybe they can talk me down from all this hscript stuff.
Currently, for other things where comparisons are necessary hxcpp automatically generates defines suffixed with +.
So for example if you want NDK >= 20 && NDK < 22, you can do:
<compilerflag value="-fno-integrated-as" if="NDKV20+" unless="NDKV22+" />
I thought about adding if and switch xml tags but realised thats madness.
We do have the section tag, which is effectively an if tag.
My idea was a script tag that you could then programatically modify the build environment through a hxcpp hscript api. e.g.
I do think having hscript would be a bit overkill, and I think it's better to avoid adding too much complexity. It becomes difficult to reason about a turing complete build system. Perhaps we could consider adding support for an alternative external build system which is a bit more flexible, if it would solve some of these issues.