Using Bindings
The following explains how to use bindings generated with Scala Native Bindgen through several examples.
A Simple Vector Library
Consider following header file:
struct point {
float x;
float y;
};
struct lineSegment {
struct point a;
struct point b;
};
float cosine(struct lineSegment *v1, struct lineSegment *v2);
Full source at GitHub
Bindgen will output
- type aliases for the structs
- binding for function
cosine
- helper functions that make usage of structs easier
package org.example
import scala.scalanative._
import scala.scalanative.native._
@native.link("vector")
@native.extern
object vector {
type struct_point = native.CStruct2[native.CFloat, native.CFloat]
type struct_lineSegment = native.CStruct2[struct_point, struct_point]
def cosine(v1: native.Ptr[struct_lineSegment], v2: native.Ptr[struct_lineSegment]): native.CFloat = native.extern
object implicits {
implicit class struct_point_ops(val p: native.Ptr[struct_point]) extends AnyVal {
def x: native.CFloat = !p._1
def x_=(value: native.CFloat): Unit = !p._1 = value
def y: native.CFloat = !p._2
def y_=(value: native.CFloat): Unit = !p._2 = value
}
implicit class struct_lineSegment_ops(val p: native.Ptr[struct_lineSegment]) extends AnyVal {
def a: native.Ptr[struct_point] = p._1
def a_=(value: native.Ptr[struct_point]): Unit = !p._1 = !value
def b: native.Ptr[struct_point] = p._2
def b_=(value: native.Ptr[struct_point]): Unit = !p._2 = !value
}
}
object struct_point {
import implicits._
def apply()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point]
def apply(x: native.CFloat, y: native.CFloat)(implicit z: native.Zone): native.Ptr[struct_point] = {
val ptr = native.alloc[struct_point]
ptr.x = x
ptr.y = y
ptr
}
}
object struct_lineSegment {
import implicits._
def apply()(implicit z: native.Zone): native.Ptr[struct_lineSegment] = native.alloc[struct_lineSegment]
def apply(a: native.Ptr[struct_point], b: native.Ptr[struct_point])(implicit z: native.Zone): native.Ptr[struct_lineSegment] = {
val ptr = native.alloc[struct_lineSegment]
ptr.a = a
ptr.b = b
ptr
}
}
}
Full source at GitHub
Using the Vector Library
Let’s write code that creates two line segments and calculates the angel between them.
First we need to create points. We will use native.Zone
to allocate struct (more information on memory management can be found here: Scala Native memory management).
The generated bindings contain helper functions that make struct allocation easier. To import them use import org.example.vector._
Let’s create two points and the first line segment:
import org.example.vector._
import scala.scalanative.native.Zone
Zone { implicit zone =>
val p1 = struct_point(1, 1)
val p2 = struct_point(7, 4)
val lineSegment1 = struct_lineSegment(p1, p2)
}
Full source at GitHub
There is no need to create points manually, just call struct_lineSegment
constructor and set point coordinates using fields setters.
Scala Native allows us to access a field by using _N
method where N
is index of a field (see Scala Native memory layout types) but it is not convenient because we have to match indices with fields names.
Bindgen provides implicit helper classes that wrap calls to _N
in functions with meaningful names. To import these classes add import org.example.vector.implicits._
to your code:
import org.example.vector.implicits._
val lineSegment2 = struct_lineSegment()
lineSegment2.a.x = 3
lineSegment2.a.y = 4
lineSegment2.b = struct_point(5, 0)
Full source at GitHubstruct_lineSegment
contains fields of value type struct_point
but setters accept variables of type native.Ptr[struct_point]
. The reason is that Scala Native does not allow passing structs and arrays by value (see scala-native/scala-native#555).
Now we can calculate the angel between the line segments:
val angle = cosine(lineSegment1, lineSegment2)
Full source at GitHub