To show the usefulness of Haxe at my company I have been working with hxcpp to compile a demo to both native using a C++ engine and Flash using a library that match the C++ engine api.
I chose to use the latest haxe/hxcpp version so I can use the new extern facility provided by hxcpp. This simplify the cpp/haxe integration and allow to manipulate cpp object directly. The help from Hugh was invaluable to get my demo working.
If you did not see it already there is Hugh’s 2014 haxe conference talk:
slides here
video here
The C++ engine I am working with takes full control of the main entry point and as such I could not use hxcpp to generate the full application binary. Instead I made use of the -D static_link
directive so hxcpp generate a static library that is then linked to the binary generated by the engine. This is quite nice since to make the complete executable, only linking is required once the engine main and hxcpp have done its job. This also means only hxcpp need to compile once the engine main has been compiled.
There are quite few engine that take control of the main entry point like that and this is due to the way each platform manage an app (IOS/Android…). So hopefully this article might help you integrate haxe with your engine. In any case most of the thing I will talk about it here apply as well to other projects where hxcpp generate the app directly (you can skip the C++ to Haxe section as you might not need it at all in that case though).
For this article I cannot show any of the engine code but basically to create a game in this engine you have the possibility to implement the required virtual function from a specific C++ abstract class. These functions are the entry points to your game specific code. There is a function for init(), one for update() and some more for events like touch events and resize and few other stuff. From there you can instantiate all the required cpp classes or services …
Since I cannot show you the C++ engine code I set up some c++ code that act in a similar fashion. I did not bother creating an abstract class but as you can guess it will work as well with such setup.
Requirements
Before starting you will need to setup your development environment. This require few steps as we are using bleeding edge stuff:
First of all you will need the latest haxe from http://build.haxe.org I have been using the build from 13/11/2014 :
for windows this is this one:
You will also need the latest hxcpp from git. I have been using the following revision :
fb6e31af65564352060e4e4781d79f342f442161
While you can clone it using git itself, I recommend doing it via haxelib:
In order to install library hosted on a git repo you can use the haxelib git command:
haxelib git <library-name> <git url>
so for hxcpp this is the following :
haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp
then you can update to the revision mentioned above
Finally you will need to compile the haxe std library so it match your application. Normally hxcpp come with library already generated but for example for Windows since we use a specific runtime (MTd) we will need to rebuild the generated library from hxcpp You will need first to go into the project folder of the hxcpp folder. hxcpp folder is located in the haxe library folder. In unix system it is usually here :
/usr/lib/haxe/lib/
For Windows this is normally here :
C:\HaxeToolkit\haxe\lib\
so here since you want to go to the hxcpp folder installed via haxelib git and you want to go to the subfolder “project” you need to go at the following path:
C:\HaxeToolkit\haxe\lib\hxcpp\git\project for windows
/usr/lib/haxe/lib/hxcpp/git/project for unix
there you execute the following command for unix :
neko build.n -debug
for windows :
neko build.n -debug -DABI=-MTd
the debug flag is not necessary but allow you to debug inside the Haxe std generated by hxcpp
One last thing I forgot to mention: you will need cmake as I use it to generate the platform specific project files for the C++ code
And this should be all for your environment setup
The Demo
Now you can grab the source code of the example at https://github.com/wighawag/hxcpp-test As said earlier, it does not use my company’s c++ engine but instead it use a simple setup where the main is not controlled by Haxe.
The cpp folder contains the “engine code” with the required haxe cpp code. the src folder contains the haxe code that will generate the static library in the “lib” folder (which will be linked to the binary generated by the “engine”)
the build.hxml have some directive you might be interested in :
-main Main #this is the main entry point of the static_library and its `static function main` will be executed first
-cpp lib # this is the folder to which the generated cpp and static library will be generated (named Main-debug.a/Main-debug.lib because of debug)
-cp src # this is the haxe source folder
-lib hxcpp # this is required to use hxcpp
-D static_link # as mentioned earlier this is required to generate a static library instead of an executable
-D ABI=-MTd # this is required on Windows. the "d" stand for debug
-debug # make it debug
You can compile your static lib via the following command :
haxe build.hxml
Then for the c++ side we use cmake and for that I choose the following process (there might be better one):
go in the cpp folder and create a build folder there (for the cmake generated files)
cd cpp
mkdir build
then go inside that build folder and execute cmake:
cd build
cmake ..
This will generate your project file in the build folder For windows it will generate by default Visual Studio solution (you will need to set HaxeTest as your startup project and then you can start debugging
C++ to Haxe
Since we do not control the main entry point we need the C++ code to call haxe code. Since we have only one haxe app running we can use static function for that direction (C++ -> Haxe). This map easily with the virtual function implementation of a main Abstract class.
Here we do not have an abstract class in C++ and instead we call directly the haxe generated cpp. You can see the main cpp code here :
In order to do that we need to add some haxe specific C++ code :
First we need to put some haxe initialization call in the c++ side : This require some declaration as you can see in the code:
extern "C" const char *hxRunLibrary();
extern "C" void hxcpp_set_top_of_stack();
then in the main we call these functions with checking for error:
hxcpp_set_top_of_stack();
const char *err = hxRunLibrary();
if (err) {
// Unhandled exceptions ...
fprintf(stderr,"Error %s\n", err );
return -1;
}
This is pretty straight forward. in your engine if you do not control the main you can put it in your init function for example
The second thing to do is to declare the haxe App static function interface :
class App_obj{
public:
static int init(int value);
static int update(::cpp::Pointer<Rectangle> rect);
};
This directly map to the haxe class App defined as shown here :
the following declaration allow you to define a specific instance which will be call for each static function
public static var instance:AppInstance;
Then you just need to set the following as your main static function :
static public function main():Void{ App.instance = new Main(); }
Please note that hxcpp append "_obj"
to every haxe class generated to cpp. That is why the declaration above mentioned "App_obj"
and not just "App"
Since in the App_obj declaration use the ::cpp::Pointer type we need to declare it as well :
namespace cpp{
template<typename T>
class Pointer{
public:
T *ptr;
inline Pointer(const T *inValue) : ptr((T*)inValue) { }
};
}
There is more to this type but we just declare what we are using.
You might be wandering why do we need to duplicate the declaration that should already exist somewhere in the hxcpp generated cpp? Actually in this particular example it might have worked but in the C++ engine I use, the hxcpp header were conflicting with some of the engine headers. I forgot the exact reason but it might be due to some shared typdef? So Instead I duplicate the headers that I need. This should not be much more that what I have here.
Once you have the declaration setup, you can make your call:
Here I did not bother to make a proper engine and i just call init() followed by update on only once but this should be pretty straightforward to extend:
::App_obj::init(1);
::App_obj::update(::cpp::Pointer<Rectangle>(rect));
This is pretty much it for having your haxe code run from the c++ engine
Haxe to C++
In order to go the other way (Haxe -> C++) we can use the latest “extern” feature of hxcpp. This is far better than the old cffi which allowed only static function to be called. If you prefer though, you can still use it.
The way it works is similar to other targets except there is few gotchas.
You declare your extern as usual with some extera metadata:
@:structAccess
@:include("Rectangle.h")
@:native("Rectangle")
extern class Rectangle{
public function set_values(w : Int, h : Int) : Void;
public function area() : Int;
}
@:structAccess need to be there so that hxcpp use the “.” operator instead of e the “->” operator
By the way, @:unreflective might be necessary in some case so hxcpp do not generate Dynamic access to the class. Do not need it here for Rectangle though.
the @:include metadata will inject the include directive in every cpp file generated that need that extern.
the @:native tell hxcpp the name (including namespace if provided) of the class. Here it is require as the extern live in the “engine” package and without that hxcpp would add use a namespace “engine”
I also added an Include.hx file which add some information to hxcpp so it can find the header files included:
@:buildXml("
<files id='haxe'>
<compilerflag value='-I../cpp/include'/>
</files>
<files id='__lib__'>
<compilerflag value='-I../cpp/include'/>
</files>
")
@:keep class Include{
}
both haxe
and __lib__
section are required. the __lib__
is used as we compile it to a static library. I could not find documentation about it though.
the @:keep is to make sure the class is used (so that hxccp use its info) even if not declared anywhere.
Once you have the extern and the extra information for hxcpp you can use the class as an Haxe object.
Here I directly use the Rectangle header file but since some of my engine headers had conflict with hxcpp I had to duplicate the problematic header for hxcpp to know about the required type in a similar way than described in the c++ -> haxe section. You might need to do that as well.
Another thing though is that you will probably want to pass pointers to haxe. In that case you will need the cpp.Pointer class and the corresponding ::cpp::Pointer, the same I use for the Rectangle instance that I pass to the update method.
With pointer you have to be careful as haxe do not modify the semantics of the cpp pointer. As such it modify Haxe semantic a bit:
When you have a cpp.Pointer in haxe code, if you access the value/ref member and store it in a variable. that variable will contain a copy of the value pointed by the pointer and not the reference. If you want to manipulate the object you have to alway use the ref inline. You can think of “ref” as the “->” of cpp.
If you take a look at App.hx you can see :
rect.ref.set_values(4,200);
instance.update(rect.ref.area());
The ref is used so the actual rect is modified and its area method called apropriately.
hxcpp could probably improve on this. Or a macro could be done to generate some code to avoid the issue: it could generate an error on assigning ref to a variable for example.
To be clear the following will set values on a copy and thus the area passed to function will not be the expected one:
var actualRect = rect.ref;
actualRect.set_values(4,200);
instance.update(rect.ref.area ());
Conclusion
There is probably a lot more to be said on the hxcpp extern integration but that would be all for now. If you knew already about cffi, I suppose this new extern facility should look like a big improvement. The integration process while not as seemless as flash/java target, it is not very hard neither. Maybe we could generate the extern from the header files directly. In any case, I hope you got some learning from reading and that you will deep into haxe/hxcpp to explore more. Thank you for reading. If you have any questions, do not hesitate to put a comment.