hxcpp icon indicating copy to clipboard operation
hxcpp copied to clipboard

HXCPP_CXX_STANDARD Define

Open Aidan63 opened this issue 1 year ago • 2 comments

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>

Aidan63 avatar Jun 27 '24 20:06 Aidan63

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.

Aidan63 avatar Jun 02 '25 18:06 Aidan63

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.

tobil4sk avatar Sep 28 '25 16:09 tobil4sk