Configuration
Bindgen provides several options for configuring what to include and how to output the generated bindings. The options can be provided to the CLI or as part of the sbt bindings declaration.
Excluding Definitions By Prefix
Definitions may be excluded by their prefix. This is useful when private definitions should not be part of the generated binding. This is often the case for definitions starting with __
.
- sbt
-
Full source at GitHubNativeBinding(pathToHeader) .excludePrefix("__")
- CLI
-
scala-native-bindgen --binding-config=/path/to/config
To exemplify consider the following header file:
int access(const char *path, int mode);
int read(int fildes, void *buf, int nbyte);
int printf(const char *restrict format, ...);
int __excluded(void);
Full source at GitHub
When the exclude prefix is set to __
, then the resulting bindings will be:
package org.example.app.stdlib
import scala.scalanative._
import scala.scalanative.native._
@native.extern
object stdlib {
def access(path: native.CString, mode: native.CInt): native.CInt = native.extern
def read(fildes: native.CInt, buf: native.Ptr[Byte], nbyte: native.CInt): native.CInt = native.extern
def printf(format: native.CString, varArgs: native.CVararg*): native.CInt = native.extern
}
Full source at GitHub
Binding Configuration File
The binding configuration is a JSON file which allows to map the path of a header file to the associated object as well as the names of the C types and symbols to their respective Scala types and definitions. The configuration file can be used when integrating with third party bindings.
- sbt
-
Full source at GitHubNativeBinding(pathToHeader) .bindingConfig(pathToConfig)
- CLI
-
scala-native-bindgen --binding-config=/path/to/config
Using Types From Third Party Bindings
If you need to generate bindings that uses types from bindings that have not been generated with scala-native-bindgen
you have to provide the mapping between the header path and the Scala object’s fully qualified name. And in some cases also the Scala name of the C types. Using the vector library example from Using Bindings, let’s assume that the vector library was created so that struct point
was named Point
:
package com.example.custom.binding
import scala.scalanative._
import scala.scalanative.native._
@native.link("vector")
@native.extern
object Vector {
type Point = native.CStruct2[native.CFloat, native.CFloat]
type LineSegment = native.CStruct2[Point, Point]
// ...
Full source at GitHub
To use this binding, create a configuration file with the folllowing content, where path
is the name of the header file (usually the part of the path inside the /usr/include
or /usr/local/include
directory), object
is the fully qualified name of the Scala object (i.e. package name as well as the Scala object name) and finally names
for each of the types:
{
"vector.h": {
"object": "com.example.custom.binding.Vector",
"names": {
"struct point": "Point",
"struct lineSegment": "LineSegment"
}
}
}
Full source at GitHub
Now in the library you are creating a binding for, any usage of struct point
:
struct circle {
struct point point;
double radius;
};
Full source at GitHub
will reference the Point
type alias inside the Vector
object:
type struct_circle = native.CStruct2[com.example.custom.binding.Vector.Point, native.CDouble]
Full source at GitHub
Using Types From the Scala Native Bindings
Similar to the above, the following example shows how you can use types defined in the C standard library and C POSIX library bindings shipped with Scala Native. Let’s assume we have a binding with a method that uses the FILE
type from <stdio.h>
:
int wordcount(struct wordcount *wordcount, FILE *file);
Full source at GitHub
We can then write a binding configuration that maps the header name to the object defined in Scala Native.
{
"stdio.h": {
"object": "scala.scalanative.native.stdio"
},
"_stdio.h": {
"object": "scala.scalanative.native.stdio"
}
}
Full source at GitHubIn the above binding configuration, we duplicate the mapping to work on both Linux and macOS since on macOS the definition of FILE
is found inside /usr/include/_stdio.h
.
The generated binding will then use the stdio.h
binding provided by Scala Native:
def wordcount(wordcount: native.Ptr[struct_wordcount], file: native.Ptr[scala.scalanative.native.stdio.FILE]): native.CInt = native.extern
Full source at GitHub
And we can use the binding by opening a file using fopen(...)
:
import org.example.wordcount.WordCount._
import scalanative.native._
Zone { implicit zone =>
val result = struct_wordcount()
val file = stdio.fopen(pathToFile, c"r")
val code = wordcount(result, file)
stdio.fclose(file)
}
Full source at GitHub