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 GitHub
Note

struct_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