ammer

Structs

When a struct type is not opaque, its fields are known and can be read and written directly, without using a library method.

To define a struct type, extend ammer.def.Struct<..., ...> with a Haxe class. The first type parameter for ammer.def.Struct should be a string identifying the native C type name. The second type parameter should be the ammer library this type belongs to.

Struct definitions can contain variable fields, as well as instance methods.

Example: library datatype definition

class FoobarStruct extends ammer.def.Struct<"struct foobar_s", Foobar> {
  // ...
}

In this example, FoobarStruct is a struct in the Foobar library. The C name for this struct is struct foobar_s. Values of FoobarStruct in Haxe represent instances of struct foobar_s* (a pointer to struct foobar_s).

Instances are pointers

Note that on the Haxe side, any struct value will be represented as a pointer to a struct. This is because most ammer targets do not support arbitrarily large stack-allocated data. See passing structs directly for declaring APIs which do not use a pointer indirection.

Variables

Structs definitions can contain variables, declared as public var or var fields.

Example: struct variables

class FoobarStruct extends ammer.def.Struct<"struct foobar_s", Foobar> {
  public var bar:Int;
}

In this example FoobarStruct has a bar variable that can be read or written:

var x:FoobarStruct = #dummy expr/*...*/;
x.bar = 3;
var y = x.bar;

Variables map to pointer accesses in C code, so a bar variable is read as (someStruct)->bar and written as (someStruct)->bar = value. Note that any read or write variable access may have a runtime cost of a function call.

Allocation and deallocation

To make it possible to allocate and deallocate a struct, it must be marked with the @:ammer.alloc metadata. When annotated, several functions are made available:

The name of the generated functions can be changed to avoid conflicts with other functions. @:ammer.alloc is simply a convenience shortcut to the combination @:ammer.gen.alloc("alloc"), @:ammer.gen.free("free"), @:ammer.gen.nullPtr("nullPtr"), where the string arguments specify the name of each generated method.

Example: allocating and deallocating a struct

Given a struct definition annotated with @:ammer.alloc:

@:ammer.alloc
class FoobarStruct extends ammer.def.Struct<"struct foobar_s", Foobar> {
  public var some_field:Int;
}

It can be allocated by calling alloc:

// with fields zeroed out:
var x = FoobarStruct.alloc();
// or with some initial values:
var x = FoobarStruct.alloc({
  some_field: 42,
});

It can then be deallocated:

x.free();

And a null pointer can be obtained:

var x = FoobarStruct.nullPtr();

Passing structs directly

Native methods which take a struct directly, as opposed to a pointer to a struct, can be declared by using the special ammer.ffi.Deref<...> type. This dereferences the struct pointer just before the native method call.

Example: using ammer.ffi.Deref

class Foobar {
  public static function take_struct_ptr(x:FoobarStruct):Void;
  public static function take_struct_val(x:ammer.ffi.Deref<FoobarStruct>):Void;
}

This example demonstrates passing a struct using a pointer and passing it directly. The corresponding C signatures could look like this:

void take_struct_ptr(struct foobar_s* x) { /*...*/ }
void take_struct_val(struct foobar_s x) { /*...*/ }

Note that on the Haxe call side, the two methods are called the same way: by passing an instance of the FoobarStruct type. The dereferencing, if any, happens transparently.

var x:FoobarStruct = #dummy expr/*...*/;
Foobar.take_struct_ptr(x);
Foobar.take_struct_val(x);

A similar situation arises when a native library method returns a struct value. To obtain a pointer to the struct, a heap allocation must take place to store that struct. In ammer, return types can be wrapped with the special ammer.ffi.Alloc<...> type to achieve this.

Example: using ammer.ffi.Alloc

class Foobar {
  public static function give_struct_ptr():FoobarStruct;
  public static function give_struct_val():ammer.ffi.Alloc<FoobarStruct>;
}

This example demonstrates a native method returning a pointer to a struct and one returning a struct directly. The corresponding C signatures could look like this:

struct foobar_s* give_struct_ptr() { /*...*/ }
struct foobar_s give_struct_val() { /*...*/ }

Note that on the Haxe call side, the two methods have the same return type: an instance of FoobarStruct. The allocation, if any, happens transparently.

var x:FoobarStruct = Foobar.give_struct_ptr();
var y:FoobarStruct = Foobar.give_struct_val();

Linking

Structs should be linked with the parent library using the @:ammer.sub(...) metadata to avoid compilation errors. See linking subdefinitions.

Metadata applicable to structs

« Previous: Opaque types Next: Instance methods »