Haxe Cppia Notes

Overview of Cppia

Cppia is an additional target for the Haxe compiler which depends on the Hxcpp runtime. On the Hxcpp side, you have a virtual machine which can interpret Cppia bytecode. Cppia bytecode is generated from standard Haxe code using the Haxe compiler just like any other backend. There isn't really any magic involved here as far as I can tell, it's just different from most other targets because it's tightly coupled to the Hxcpp backend.

One of the coolest benefits of Cppia is that it's a system target (so it has access to the same APIs as the Cpp target), and it compiles extremely quickly. This lets you do rapid iteration on a native target, much like the Neko or Hashlink targets. However, unlike Neko and Hashlink, you can load and execute Cppia code during runtime from your Hxcpp application very easily. This can be used for scripting, for example.

If you were to try to implement scripting like that using Neko or Hashlink, you would need to embed a Neko or Hashlink virtual machine into your program manually, and maybe add a way to load/execute those scripts from your Haxe code. This is a totally viable option of course (and the Hashlink target might even perform better than Cppia [this is unconfirmed]), but it's a lot of work. With Cppia, all you have to do is add the -D scriptable flag to your host application (which causes the Cppia interpreter to be compiled), and then use the cpp.cppia.* APIs in the standard library to load/run/reflect on your scripts.

Another benefit is that since Cppia is compiled from standard Haxe code, you could use Cppia during development to iterate rapidly, and then generate C++ code when its time to ship so that you get maximum performance and avoid having to include the Cppia interpreter in your final build.

Using Cppia

Compiling Cppia scripts

To compile Haxe code as Cppia, you use the -cppia flag. For example:

haxe -main MyScript.hx -cppia ./build/myscript.cppia

This will generate myscript.cppia in a folder called build. Since this is a standard Haxe program, MyScript should have a public static function main():Void function.

Running Cppia scripts

To run a Cppia script, you need a Cppia Host. This is any hxcpp program that includes the Cppia virtual machine. To create one, you just need to add the -D scriptable definition to the Haxe compiler when using the hxcpp target. Example:

haxe -main MyHost.hx -D scriptable -cpp ./build

This will compile the program with support for running Cppia scripts, but you still have to run them yourself somehow in your code. Simply adding the scriptable flag won't automatically turn your program into a command-line script runner. To run a Cppia script, you have to read the contents (as a text file), and use it with the cpp.cppia.* APIs. For example:

import cpp.cppia.Host;

class MyHost {
    public static function main():Void{
        var src:String = sys.io.File.getContent("myscript.cppia"); //load script contents into string
        Host.run(src); //automatically initialize and run the entrypoint (static main function)

And that's all there is to it. If you need more control, like reflection on types in the script, you can use the cpp.cppia.Module class. See the source for the cpp.cppia.Host.run function for an example of how to initialize and run a Module instance. It's super simple, and then once you have an instance you can use the function cpp.cppia.Module.resolveClass to find types defined in the script. Those types can then be used with Haxe's standard Type.createInstance and similar functions.

Using Libraries in Cppia

Cppia scripts are standard Haxe, so you could probably use any existing Haxe library with it. You could probably even use OpenFL, although OpenFL is a huge library and you'd be better off keeping that in your host rather than distributing it in every single script.

But then this raises the question: how do you use library types in a Cppia script if you don't want to compile that library along with your script?

The answer is that Cppia offers a way to exclude classes that are already in the Host. So you're free to add any haxelib to your cppia script using the standard -lib flag. This makes those types available to you within the script, but doesn't actually compile them. It will assume that those types are available in the Host, so they will try to be resolved at runtime.

So how does this work? When you compile your host with the -D scriptable flag, the compiler will generate a file called export_classes.info. This contains a list of every class included in the Host. You can use this file when compiling your Cppia script to inform the compiler as to which classes can be safely excluded from the compilation.

How to exclude classes with export_classes.info

The Cppia backend includes a macro which will automatically search for a file called export_classes.info in the classpath. That part is in bold because it is important: the file MUST be in your classpath (a folder can be added to the classpath with the -cp flag)

Don't assume that the Host will generate that file in the correct place, because it probably won't. You need to make sure that you copy that file into the classpath of your Cppia script. You could automate the process of copying export_classes.info into the correct place if desired.

An alternative method is to not rely on that search function and just manually specify the path to that file. This can be done with the -D dll_import=path/to/file.info flag. You would need to add that flag to your Cppia compile command.


If you see any errors here or have comments, feel free to contact me via email or Twitter.