C API Documentation - HaxeFoundation/hashlink GitHub Wiki

When you #include <hl.h> you get access to various HashLink API calls that can be used to implement bindings.

The full source is available here

You can look at various examples of how it is used in standard library and additional libraries

A few additional notes are following.

Memory allocation

You can allocate different kinds of memory in HashLink:

  • hl_gc_alloc_noptr(size) allows you to allocate "opaque" memory that should not contain any pointer. It is used for example for string representation or numerical arrays / binary data. It will not be scanned by the garbage collector.

  • hl_gc_alloc_raw(size) allocates memory that might contain HashLink pointers. For instance an array of 4 HL values. Please note that storing non-memory pointers inside this memory (int or doubles for example) might have them mistaken for actual pointers to values in the HL memory heap, creating false-positive memory leaks.

  • hl_gc_alloc_finalizer(size) similar to hl_gc_alloc_noptr but the first pointer in the memory should be a void finalize( void * ) function that will be called (unless NULL) when the memory block gets garbage collected. This can be useful to automatically release some underlying resources (closing files, deleting C++ objects, etc.) when it's no longer reachable from Haxe code.

  • hl_gc_alloc(type, size) allocates memory for a given type. The first pointer in memory will store the hl_type* that will help the garbage collector scan its content. You rarely have to use this as objects are usually allocated on the Haxe side.

Allocating HL values

The following methods allow you to allocate HL values instead of raw memory:

HL_API varray *hl_alloc_array( hl_type *t, int size );
HL_API vdynamic *hl_alloc_dynamic( hl_type *t );
HL_API vdynamic *hl_alloc_obj( hl_type *t );
HL_API venum *hl_alloc_enum( hl_type *t, int index );
HL_API vvirtual *hl_alloc_virtual( hl_type *t );
HL_API vdynobj *hl_alloc_dynobj();
HL_API vbyte *hl_alloc_bytes( int size );

HL_API vclosure *hl_alloc_closure_void( hl_type *t, void *fvalue );
HL_API vclosure *hl_alloc_closure_ptr( hl_type *fullt, void *fvalue, void *ptr );

Preventing objects from being garbage collected

If you store an HL value in a static var or into an object not reachable from another HL value, you need to make sure it is reachable by the garbage collector.

This consists in adding a GC "root" that will be scanned. You can use hl_add_root to add such root, and hl_remove_root to remove it, such as the following example shows:

static vdynamic *hl_value_store = NULL;
int foo( vdynamic *v ) {
    if( hl_value_store == NULL ) hl_add_root(&hl_value_store);
    hl_value_store = v;
}

Accessing objects from C

Although it is possible to directly access the Haxe/HL objects as C structures by simply adding hl_type* field, this becomes more complex if you want to allow dynamic objects or Haxe anonymous structures as well.

You can then use these methods to access object fields whatever their actual representation:

HL_API int hl_dyn_geti( vdynamic *d, int hfield, hl_type *t );
HL_API void *hl_dyn_getp( vdynamic *d, int hfield, hl_type *t );
HL_API float hl_dyn_getf( vdynamic *d, int hfield );
HL_API double hl_dyn_getd( vdynamic *d, int hfield );

HL_API void hl_dyn_seti( vdynamic *d, int hfield, hl_type *t, int value );
HL_API void hl_dyn_setp( vdynamic *d, int hfield, hl_type *t, void *ptr );
HL_API void hl_dyn_setf( vdynamic *d, int hfield, float f );
HL_API void hl_dyn_setd( vdynamic *d, int hfield, double v );

The field is a hashed value that can be obtained using hl_hash_utf8("myfield") It is necessary to pass the type that we read / write when we use a pointer or integer (see below).

Calling Methods

When functions are passed from Haxe to HL, they use the vclosure* type.

If this is a static method (not a closure on an object instance), which you can check with c->hasValue == 0 you can safely cast the c->fun ptr to the corresponding C function to call it. Since HashLink follows the platform C calling conventions, it should work directly.

When calling more complex methods which might involve casting and closure instances, you can use the high-level method that will perform all the necessary coerces:

HL_API vdynamic *hl_dyn_call( vclosure *c, vdynamic **args, int nargs );

HL Types

The following hl_type values are available:

HL_API hl_type hlt_void;
HL_API hl_type hlt_i32;
HL_API hl_type hlt_i64;
HL_API hl_type hlt_f64;
HL_API hl_type hlt_f32;
HL_API hl_type hlt_dyn;
HL_API hl_type hlt_array;
HL_API hl_type hlt_bytes;
HL_API hl_type hlt_dynobj;
HL_API hl_type hlt_bool;
HL_API hl_type hlt_abstract;
⚠️ **GitHub.com Fallback** ⚠️