If you have ever tried to write a Haxe/ndll binding, you know it so much repetitive; create every Haxe wrapper class with all theirs fields with primitive calls static variables for load primitives and code the primitives with C/C++. This util will help on do it easier and faster.
Before: Haxe side:
package wrapper; #if cpp import cpp.Lib; #elseif neko import neko.Lib; #end class Sample { static var sum = Lib.load( "Simple", 'sum', 2 ); public static function doSum( a : Int, b : Int ) { return sum( a, b ); } }
C++ side:
#define IMPLEMENT_API #include <hx/CFFI.h> value sum(value a, value b) { if( !val_is_int(a) || !val_is_int(b) ) return val_null; return alloc_int(val_int(a) + val_int(b)); } DEFINE_PRIM( sum, 2 );
After:
package wrapper; @:ndll(lib="Simple") class Sample { @:ndll public static function sum( a : Int, b : Int ) : Int { } }
C++ side:
#define IMPLEMENT_API #include "hxndll.h" inline int wrapper_sample_sum(int a, int b) { return a + b; } HXNDLL_DEFINE_PRIM( wrapper_sample_sum, 2 );
Tip: You can see this complete fully functional example at test/sample directory. To Run it execute next at root path (optionals between square brackets: '[' and ']'):
haxelib run hxcpp build_ndll.xml [-DHXCPP_M64] haxe test.hxml [-D HXCPP_M64] cd bin ./TestMain[.exe]
To install Haxe-ndll, in command prompt run this:
haxelib install hxndll
And that's all. Ready to use.
First, you have to setup your project and there are at least 3 way to do that
-
In you
project.hxml
add:
--macro hxndll.Compiler.process(['<path/to/src>', <path/to/another/src>, ...])
-
Implementing
hxndll.Importer
each class you want to process.
It uses@:autoBuild
metatdata, see http://haxe.org/manual/macros/build#autobuild for more information. -
At each class you want to process add metadata:
@:build(hxndll.Transformer.build())
Personally I prefer method 1 or 3, because they leave no footprints at final code.
Finally, you must add @:ndll
or @:ndll_use
metadata to your class in order to proccess it. Further on you will find complete info about it here.
Tip: You only can use Haxe Ndll with classes.
Let's get down to work on Haxe classes:
###First reduction: @:ndll_import
###
FFun(static, empty) -> FVar(static, w/ prim load)FVar(static, empty) -> FVar(static, w/ prim load)
It is the easiest and simplest reduction and it is used to load a primitive. i.e.:
@:ndll_import(lib="waxe") static function wx_window_create( args : Array ) : Dynamic { }
or
@:ndll(lib="waxe") static var wx_window_create : Array -> Dynamic;
They are equivalent and produce this field:
static var wx_window_create : Array -> Dynamic = cpp.Lib.load("waxe", "wx_window_create", 1);
HxNdll helps to forget the porting tricks, you don't need to worry about them. So forget to import cpp.Lib
or neko.Lib
, HxNdll will do it for you depending on target.
Tip: @:ndll_import
can be use with static functions or static variables.
Tip: Only with static variables, mere @:ndll
means @:ndll_import
####@:ndll_import
parameter list####
lib
: typeString
, default is user defined otherwise throws an error.
Its value is some library file name (without extension ".ndll") i.e.:lib="waxe"
prefix
: typeString
, default is user defined or autogenerated (see @:ndll_use section for more info).
Its value is first part of primitive name. i.e: "wx_window_"name
: typeString
, default is field name.
Its value is last part of primitive name and it will be transformed to underscore case. i.e.: "getSize" will become to "get_size".
So, previous example becames:
@:ndll_import(lib="waxe", prefix="wx_window_") static function create( args : Array ) : Dynamic { }
###Get shorter: User-defined defaults###
The main goal of HxNdll is to write more with less. When you code a wrapper class usually all its methods have a pattern, I mean, they have the @:ndll
metadata with almost the same parameters.
Here is when @:ndll_use
class metadata appears.
Tip: class metadatas @:ndll_use
and @:ndll
are the same thing, although the first is verbose.
####@:ndll_use
default parameter list####
lib
: typeString
. Its value is some library file nameprefix
: typeString
, default is user defined otherwise class path is used. Its value is first part of primitive name and it will transformed to underscore case. i.e.: some.pack.MyClass will became to "some_pack_my_class_". Note underscore as last character.params
: typeArray<Expr>
, default is[]
(empty array).
Its value is an ordered list of arguments which will be merged with field arguments. See Paramaters Merging section.
###Second reduction: @:ndll_forward
###
/-> FFun(w/ prim call) FFun(empty) -| \-> FVar(static, w/ prim load)
@:ndll_forward
generates primitive load and fills function block with primitive call.
###Third reduction: @:ndll_prop
###
/-> FProp(w/ setter or getter) |-> nil or setter /-> FFun(w/ prim call) FProp(w/ setter or getter) -| \-> FVar(static, w/ prim load) \-> nil or getter /-> FFun(w/ prim call) \-> FVar(static, w/ prim load)