Discussion:
hxcpp native access
Gamehaxe
2011-11-06 16:33:47 UTC
Permalink
Hi,
I have been doing a bit of work on native code access from within hxcpp.
You will need the latest svn haxe and hxcpp versions to try this out.

This is done with class & function meta-data to inject code into the
generated source files.

For a class, there is

@:headerCode("code") // Inject code into class header file - eg for types
of injected members

@:headerClassCode("code") // Inject code into header class definition - eg
member variables/functions

@:cppFileCode("code") // Inject code into top of cpp file - eg local
includes, local functions, static variables

for functions:

@:functionCode("code") // Inject code into top of function - eg, whole
implementation

@:functionTailCode("code") // Inject code into end of function - eg, close
functionCode, or continue processing

In addition, you can inject code into the bottom of the build.xml code via
the class meta-data:
@:buildXml('xml fragment')

The whole process can be made to work, but there may be a little bit of
pain involved.
One problem you will come across is dealing with the hxcpp types - mostly
these
are obvious, but you may need to study a bit of generated code to find out
the
details. eg, using the ".__s" member of a string to access the const char
*pointer.

You also need to consider the compiler environment of other peoples
computers,
and the compiling dependencies (eg, headers/libraries) when designing a
system.

Garbage collection is also an important consideration - I will present
here two
possible ways of handling this.




// This is so the "FILE *" type is known to the compiler. Other libraries
may require other includes.
@:headerCode("#include <stdio.h>")
// Define the GC finalizer for method 1 - code goes in the cpp file .....
@:cppFileCode('void staticClose(MyStdio_obj *inObj) { if (inObj->mFile)
fclose(inObj->mFile); printf("CLOSED!\\n"); }\n')
// Put a "FILE *" member in the class, as well as an inline function.
// All members get "memset" to 0 on construction - no automatic destructor
will get called...
@:headerClassCode('FILE *mFile;\ninline static void Print() {
printf("PRINT!\\n"); }')
class MyStdio
{
public function new()
{
// You can sometimes call/use the native members directly if the
usage matches standard
// haxe usage. Use the 'untyped' keyword so that the compiler does
not check
// that MyStdio has a "Print" function.
untyped MyStdio.Print();
// Can't do code-injection on constructor - must use separate
function
setFinalizer();
}

@:functionCode("hx::GCSetFinalizer(this, (hx::finalizer)staticClose);")
function setFinalizer() { }

@:functionCode('mFile=::fopen(filename.__s, mode.__s); return
mFile!=0;')
public function fopen(filename:String, mode:String) { return false; }

@:functionCode('return fwrite(inString.__s, inString.length, 1,
mFile);')
public function writeString(inString:String) { return 0; }

@:functionCode('if (mFile) fclose(mFile); mFile=0;')
public function close()
{
// Ok to do memory operations here (not a native finalizer)
trace("Closed!");
}
}


class Test
{

// This is put in a separate function to ensure the stack is clean of
// stray references to the 'file'.
public static function testFile()
{
var file = new MyStdio();
file.fopen("test.txt", "wb");
file.writeString("Hello, World!\n");
file = null;
}

public static function main()
{
testFile();
cpp.vm.Gc.run(true);
trace("Done.");
}


}


This uses native code for the "worker" functions, and an external
finalizer,
which closes any file left open.

You have to be careful in the finalizer method to not perform and hxcpp
memory
allocations. You also have to be careful to to the "minimum amount"
because
the function could be called from a different thread, and this can cause
problems for quite a few systems (eg, opengl).

An alternate way to manage collected objects is to use the "zombie" method.
Here, you use haxe, rather than native, code to create the finalizer, ie,
replace:

setFinalizer();

With:

cpp.vm.Gc.doNotKill(this);

This means the GC will not collect this object, but instead place it in
limbo
on the "zombie list", which you can access any time with something like
this:

while(true)
{
var zombie = cpp.vm.Gc.getNextZombie();
if (zombie==null)
break;
var close = zombie.close;
if (close!=null)
close();
}

There is nothing special about the "close" name - each object can be
treated
separately. The advantage of this is that you process the zombies whenever
you want, using the correct thread and not having to worry about memory
allocations. You can even reanimate the zombie by recording a reference to
it - but you will need to call doNotKill if you do not want it to die
next time its collected. This can be performed, say, once per frame in
an NME-style application.

If you have a library with include files and libraries, you can add
xml to the end of the build.xml file by using meta data on one of
your classes like this:

@:buildXml('<include name="${haxelib:yourLib}/native.xml"/>')

And then include a "native.xml" file in your haxelib.
This can add "-Iinclude" and "-lib:abc" to the build system.

You could list out the xml directly, or use "${MY_XML}" or something else.

In general, I think is is probably best to put as much as possible in
external
files, rather than trying to inline too much ascii.

Hugh
--
haXe - an open source web programming language
http://haxe.org
Nicolas Cannasse
2011-11-06 20:10:34 UTC
Permalink
Post by Gamehaxe
Hi,
I have been doing a bit of work on native code access from within hxcpp.
You will need the latest svn haxe and hxcpp versions to try this out.
This is done with class & function meta-data to inject code into the
generated source files.
For a class, there is
@:headerCode("code")
[...]

Could you document these here (in a cpp-specific section) :
http://haxe.org/manual/tips_and_tricks

Best,
Nicolas
--
haXe - an open source web programming language
http://haxe.org
CauĂȘ Waneck
2011-11-07 01:09:40 UTC
Permalink
Wow, Hugh!

That's great news! I was actually creating a library to do something like
that here :

http://code.google.com/p/native-gateway/

you can see some examples here:
http://code.google.com/p/native-gateway/source/browse/trunk/test/comtacti/native/tests/TestBasicInlined.hx
http://code.google.com/p/structs/source/browse/trunk/src/cpp/MemoryAccess.hx
auto cffi wrapper creation -
http://code.google.com/p/native-gateway/source/browse/trunk/test/comtacti/native/tests/TestBasicCffi.hx

it involved running a custom build tool - forked from nme's - that added
the possibility of having pre-build and post-build scripts running. It also
looked on all -lib projects for include .xml files so you could use e.g. a
library that needed some postbuild/prebuild script

Anyway, I really like your way much better (of course), though I'd request
a __cpp__() access also, specially with the syntax (which you can see at
the second link), so we can interoperate more easily with hand-written vs
hxcpp code

Also in the future I plan to add dll importing with hxffi just like C#
does. : ) What do you think about this?

Thanks for everything! This is awesome!
Caue
Hi,
Post by Gamehaxe
I have been doing a bit of work on native code access from within hxcpp.
You will need the latest svn haxe and hxcpp versions to try this out.
This is done with class & function meta-data to inject code into the
generated source files.
For a class, there is
@:headerCode("code")
[...]
http://haxe.org/manual/tips_**and_tricks<http://haxe.org/manual/tips_and_tricks>
Best,
Nicolas
--
haXe - an open source web programming language
http://haxe.org
Loading...