Structs are converted to opaque types
For those types, we generate getters, setters, Tag
definition, and two apply
methods:
- A constructor, which takes all the parameters as arguments
- An allocator, which only creates an instance on the heap, without initialising it
Source C
code
typedef struct {
long long number;
} Small;
typedef struct {
int x;
char* hello;
Small sm;
} Big;
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object structs:
import _root_.libtest.structs.*
/**
* [bindgen] header: /tmp/6881480384428888469header.h
*/
opaque type Big = CStruct3[CInt, CString, Small]
object Big:
given _tag: Tag[Big] = Tag.materializeCStruct3Tag[CInt, CString, Small]
def apply()(using Zone): Ptr[Big] = scala.scalanative.unsafe.alloc[Big](1)
def apply(x : CInt, hello : CString, sm : Small)(using Zone): Ptr[Big] =
val ____ptr = apply()
(!____ptr).x = x
(!____ptr).hello = hello
(!____ptr).sm = sm
____ptr
extension (struct: Big)
def x : CInt = struct._1
def x_=(value: CInt): Unit = !struct.at1 = value
def hello : CString = struct._2
def hello_=(value: CString): Unit = !struct.at2 = value
def sm : Small = struct._3
def sm_=(value: Small): Unit = !struct.at3 = value
/**
* [bindgen] header: /tmp/6881480384428888469header.h
*/
opaque type Small = CStruct1[CLongLong]
object Small:
given _tag: Tag[Small] = Tag.materializeCStruct1Tag[CLongLong]
def apply()(using Zone): Ptr[Small] = scala.scalanative.unsafe.alloc[Small](1)
def apply(number : CLongLong)(using Zone): Ptr[Small] =
val ____ptr = apply()
(!____ptr).number = number
____ptr
extension (struct: Small)
def number : CLongLong = struct._1
def number_=(value: CLongLong): Unit = !struct.at1 = value
object types:
export _root_.libtest.structs.*
object all:
export _root_.libtest.structs.Big
export _root_.libtest.structs.Small
You can disable constructor generation
Some bindings (libnotify, gtk, nuklear) have such deep type hierarchies, that apply methods on some structs trigger an exception during bytecode generation (when compiling the generated code):
java.lang.IllegalArgumentException: UTF8 string too large
To work around it, you can disable constructor generation by passing a comma-separated
list of struct names using the --render.no-constructor
option in CLI, and noConstructor
parameter in the Binding(...)
specification.
Source C
code
typedef struct {
int x;
char* hello;
} Enabled;
typedef struct {
int x;
char* hello;
} Disabled;
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object structs:
import _root_.libtest.structs.*
/**
* [bindgen] header: /tmp/17818261139666314257header.h
*/
opaque type Disabled = CStruct2[CInt, CString]
object Disabled:
given _tag: Tag[Disabled] = Tag.materializeCStruct2Tag[CInt, CString]
def apply()(using Zone): Ptr[Disabled] = scala.scalanative.unsafe.alloc[Disabled](1)
extension (struct: Disabled)
def x : CInt = struct._1
def x_=(value: CInt): Unit = !struct.at1 = value
def hello : CString = struct._2
def hello_=(value: CString): Unit = !struct.at2 = value
/**
* [bindgen] header: /tmp/17818261139666314257header.h
*/
opaque type Enabled = CStruct2[CInt, CString]
object Enabled:
given _tag: Tag[Enabled] = Tag.materializeCStruct2Tag[CInt, CString]
def apply()(using Zone): Ptr[Enabled] = scala.scalanative.unsafe.alloc[Enabled](1)
def apply(x : CInt, hello : CString)(using Zone): Ptr[Enabled] =
val ____ptr = apply()
(!____ptr).x = x
(!____ptr).hello = hello
____ptr
extension (struct: Enabled)
def x : CInt = struct._1
def x_=(value: CInt): Unit = !struct.at1 = value
def hello : CString = struct._2
def hello_=(value: CString): Unit = !struct.at2 = value
object types:
export _root_.libtest.structs.*
object all:
export _root_.libtest.structs.Disabled
export _root_.libtest.structs.Enabled
Unions are converted to opaque types
For a union with N
members, N
constructors will be generated,
along with getters and setters.
Source C
code
typedef struct {
long long number;
} Small;
typedef union {
int x;
char* hello;
Small sm;
} Big;
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object structs:
import _root_.libtest.structs.*
import _root_.libtest.unions.*
/**
* [bindgen] header: /tmp/17844328386082372745header.h
*/
opaque type Small = CStruct1[CLongLong]
object Small:
given _tag: Tag[Small] = Tag.materializeCStruct1Tag[CLongLong]
def apply()(using Zone): Ptr[Small] = scala.scalanative.unsafe.alloc[Small](1)
def apply(number : CLongLong)(using Zone): Ptr[Small] =
val ____ptr = apply()
(!____ptr).number = number
____ptr
extension (struct: Small)
def number : CLongLong = struct._1
def number_=(value: CLongLong): Unit = !struct.at1 = value
object unions:
import _root_.libtest.structs.*
import _root_.libtest.unions.*
/**
* [bindgen] header: /tmp/17844328386082372745header.h
*/
opaque type Big = CArray[Byte, Nat._8]
object Big:
given _tag: Tag[Big] = Tag.CArray[CChar, Nat._8](Tag.Byte, Tag.Nat8)
def apply()(using Zone): Ptr[Big] =
val ___ptr = alloc[Big](1)
___ptr
@scala.annotation.targetName("apply_x")
def apply(x: CInt)(using Zone): Ptr[Big] =
val ___ptr = alloc[Big](1)
val un = !___ptr
un.at(0).asInstanceOf[Ptr[CInt]].update(0, x)
___ptr
@scala.annotation.targetName("apply_hello")
def apply(hello: CString)(using Zone): Ptr[Big] =
val ___ptr = alloc[Big](1)
val un = !___ptr
un.at(0).asInstanceOf[Ptr[CString]].update(0, hello)
___ptr
@scala.annotation.targetName("apply_sm")
def apply(sm: Small)(using Zone): Ptr[Big] =
val ___ptr = alloc[Big](1)
val un = !___ptr
un.at(0).asInstanceOf[Ptr[Small]].update(0, sm)
___ptr
extension (struct: Big)
def x : CInt = !struct.at(0).asInstanceOf[Ptr[CInt]]
def x_=(value: CInt): Unit = !struct.at(0).asInstanceOf[Ptr[CInt]] = value
def hello : CString = !struct.at(0).asInstanceOf[Ptr[CString]]
def hello_=(value: CString): Unit = !struct.at(0).asInstanceOf[Ptr[CString]] = value
def sm : Small = !struct.at(0).asInstanceOf[Ptr[Small]]
def sm_=(value: Small): Unit = !struct.at(0).asInstanceOf[Ptr[Small]] = value
object types:
export _root_.libtest.structs.*
export _root_.libtest.unions.*
object all:
export _root_.libtest.structs.Small
export _root_.libtest.unions.Big
Simple functions are converted to direct @extern
functions
Where "Simple" means "not passing naked structs
", because Scala Native cannot handle that (passing by pointer is okay)
Source C
code
typedef struct {
long long number;
} Small;
long simple(int x, char *y);
Small* with_pointers(Small *x, int y);
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object structs:
import _root_.libtest.structs.*
/**
* [bindgen] header: /tmp/9954985728506178638header.h
*/
opaque type Small = CStruct1[CLongLong]
object Small:
given _tag: Tag[Small] = Tag.materializeCStruct1Tag[CLongLong]
def apply()(using Zone): Ptr[Small] = scala.scalanative.unsafe.alloc[Small](1)
def apply(number : CLongLong)(using Zone): Ptr[Small] =
val ____ptr = apply()
(!____ptr).number = number
____ptr
extension (struct: Small)
def number : CLongLong = struct._1
def number_=(value: CLongLong): Unit = !struct.at1 = value
@extern
private[libtest] object extern_functions:
import _root_.libtest.structs.*
/**
* [bindgen] header: /tmp/9954985728506178638header.h
*/
def simple(x : CInt, y : CString): CLongInt = extern
/**
* [bindgen] header: /tmp/9954985728506178638header.h
*/
def with_pointers(x : Ptr[Small], y : CInt): Ptr[Small] = extern
object functions:
import _root_.libtest.structs.*
import extern_functions.*
export extern_functions.*
object types:
export _root_.libtest.structs.*
object all:
export _root_.libtest.structs.Small
export _root_.libtest.functions.simple
export _root_.libtest.functions.with_pointers
Problematic functions generate C forwarders
Where "Problematic" means having a struct as one of its arguments or return type. In this case we generate several variations of public Scala functions, but they all delegate to an external C function which takes its arguments as pointers, and (optionally) returns its value in a provided heap-allocated location.
Source C
code
typedef struct {
long long number;
} Small;
void bad_arguments(Small n, Small n2);
Small bad_return_type();
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object structs:
import _root_.libtest.structs.*
/**
* [bindgen] header: /tmp/3699064594116550973header.h
*/
opaque type Small = CStruct1[CLongLong]
object Small:
given _tag: Tag[Small] = Tag.materializeCStruct1Tag[CLongLong]
def apply()(using Zone): Ptr[Small] = scala.scalanative.unsafe.alloc[Small](1)
def apply(number : CLongLong)(using Zone): Ptr[Small] =
val ____ptr = apply()
(!____ptr).number = number
____ptr
extension (struct: Small)
def number : CLongLong = struct._1
def number_=(value: CLongLong): Unit = !struct.at1 = value
@extern
private[libtest] object extern_functions:
import _root_.libtest.structs.*
private[libtest] def __sn_wrap_libtest_bad_arguments(n : Ptr[Small], n2 : Ptr[Small]): Unit = extern
private[libtest] def __sn_wrap_libtest_bad_return_type(__return : Ptr[Small]): Unit = extern
object functions:
import _root_.libtest.structs.*
import extern_functions.*
export extern_functions.*
/**
* [bindgen] header: /tmp/3699064594116550973header.h
*/
def bad_arguments(n : Small, n2 : Small)(using Zone): Unit =
val __ptr_0: Ptr[Small] = alloc[Small](2)
!(__ptr_0 + 0) = n
!(__ptr_0 + 1) = n2
__sn_wrap_libtest_bad_arguments((__ptr_0 + 0), (__ptr_0 + 1))
/**
* [bindgen] header: /tmp/3699064594116550973header.h
*/
def bad_arguments(n : Ptr[Small], n2 : Ptr[Small]): Unit =
__sn_wrap_libtest_bad_arguments(n, n2)
/**
* [bindgen] header: /tmp/3699064594116550973header.h
*/
def bad_return_type()(__return : Ptr[Small]): Unit =
__sn_wrap_libtest_bad_return_type(__return)
/**
* [bindgen] header: /tmp/3699064594116550973header.h
*/
def bad_return_type()(using Zone): Small =
val __ptr_0: Ptr[Small] = alloc[Small](1)
__sn_wrap_libtest_bad_return_type((__ptr_0 + 0))
!(__ptr_0 + 0)
object types:
export _root_.libtest.structs.*
object all:
export _root_.libtest.structs.Small
export _root_.libtest.functions.bad_arguments
export _root_.libtest.functions.bad_return_type
Generated C
code
#include <string.h>
void __sn_wrap_libtest_bad_arguments(Small *n, Small *n2) {
bad_arguments(*n, *n2);
};
void __sn_wrap_libtest_bad_return_type(Small *____return) {
Small ____ret = bad_return_type();
memcpy(____return, &____ret, sizeof(Small));
}
Enums are generated for specific C type
Whatever type clang reports for a particular enum - that's the type that will be used for the enum.
This is important, as on Windows enums are int
by default, whereas on Linux and OS X they are unsigned int
(if there's no negative elements).
This documentation is built on Linux.
Source C
code
typedef enum {
U_X = 1,
U_Y = 4,
U_Z = 228
} MyUnsigned;
typedef enum {
X = -1,
Y = 4,
Z = 228
} MySigned;
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object predef:
private[libtest] trait CEnum[T](using eq: T =:= Int):
given Tag[T] = Tag.Int.asInstanceOf[Tag[T]]
extension (inline t: T)
inline def int: CInt = eq.apply(t)
inline def value: CInt = eq.apply(t)
private[libtest] trait CEnumU[T](using eq: T =:= UInt):
given Tag[T] = Tag.UInt.asInstanceOf[Tag[T]]
extension (inline t: T)
inline def int: CInt = eq.apply(t).toInt
inline def uint: CUnsignedInt = eq.apply(t)
inline def value: CUnsignedInt = eq.apply(t)
object enumerations:
import predef.*
/**
* [bindgen] header: /tmp/15997492862497794277header.h
*/
opaque type MySigned = CInt
object MySigned extends CEnum[MySigned]:
given _tag: Tag[MySigned] = Tag.Int
inline def define(inline a: CInt): MySigned = a
val X = define(-1)
val Y = define(4)
val Z = define(228)
inline def getName(inline value: MySigned): Option[String] =
inline value match
case X => Some("X")
case Y => Some("Y")
case Z => Some("Z")
case _ => _root_.scala.None
extension (a: MySigned)
inline def &(b: MySigned): MySigned = a & b
inline def |(b: MySigned): MySigned = a | b
inline def is(b: MySigned): Boolean = (a & b) == b
/**
* [bindgen] header: /tmp/15997492862497794277header.h
*/
opaque type MyUnsigned = CUnsignedInt
object MyUnsigned extends CEnumU[MyUnsigned]:
given _tag: Tag[MyUnsigned] = Tag.UInt
inline def define(inline a: Long): MyUnsigned = a.toUInt
val U_X = define(1)
val U_Y = define(4)
val U_Z = define(228)
inline def getName(inline value: MyUnsigned): Option[String] =
inline value match
case U_X => Some("U_X")
case U_Y => Some("U_Y")
case U_Z => Some("U_Z")
case _ => _root_.scala.None
extension (a: MyUnsigned)
inline def &(b: MyUnsigned): MyUnsigned = a & b
inline def |(b: MyUnsigned): MyUnsigned = a | b
inline def is(b: MyUnsigned): Boolean = (a & b) == b
object types:
export _root_.libtest.enumerations.*
object all:
export _root_.libtest.enumerations.MySigned
export _root_.libtest.enumerations.MyUnsigned
Function pointers are defined as opaque types
The inlining in apply
method is important - it's a restricting of Scala Native
that the function must be statically known.
Source C
code
typedef void* Cursor;
typedef int (*Visitor)(Cursor*);
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object aliases:
import _root_.libtest.aliases.*
/**
* [bindgen] header: /tmp/9135371491513282411header.h
*/
opaque type Cursor = Ptr[Byte]
object Cursor:
given _tag: Tag[Cursor] = Tag.Ptr(Tag.Byte)
inline def apply(inline o: Ptr[Byte]): Cursor = o
extension (v: Cursor)
inline def value: Ptr[Byte] = v
/**
* [bindgen] header: /tmp/9135371491513282411header.h
*/
opaque type Visitor = CFuncPtr1[Ptr[Cursor], CInt]
object Visitor:
given _tag: Tag[Visitor] = Tag.materializeCFuncPtr1[Ptr[Cursor], CInt]
inline def fromPtr(ptr: Ptr[Byte] | Ptr[?]): Visitor = CFuncPtr.fromPtr(ptr.asInstanceOf[Ptr[Byte]])
inline def apply(inline o: CFuncPtr1[Ptr[Cursor], CInt]): Visitor = o
extension (v: Visitor)
inline def value: CFuncPtr1[Ptr[Cursor], CInt] = v
inline def toPtr: Ptr[?] = CFuncPtr.toPtr(v)
object types:
export _root_.libtest.aliases.*
object all:
export _root_.libtest.aliases.Cursor
export _root_.libtest.aliases.Visitor
Recursive structs are rewritten with opaque pointers
This is invisible to the user, so doesn't impact the experience, but can complicate reading the code.
Scala cannot have recursive type aliases.
Source C
code
typedef struct Arr;
typedef struct {
struct Arr* nested;
} Arr;
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object structs:
import _root_.libtest.structs.*
/**
* [bindgen] header: /tmp/9093891723095626608header.h
*/
opaque type Arr = CStruct1[Ptr[Byte]]
object Arr:
given _tag: Tag[Arr] = Tag.materializeCStruct1Tag[Ptr[Byte]]
def apply()(using Zone): Ptr[Arr] = scala.scalanative.unsafe.alloc[Arr](1)
def apply(nested : Ptr[Arr])(using Zone): Ptr[Arr] =
val ____ptr = apply()
(!____ptr).nested = nested
____ptr
extension (struct: Arr)
def nested : Ptr[Arr] = struct._1.asInstanceOf[Ptr[Arr]]
def nested_=(value: Ptr[Arr]): Unit = !struct.at1 = value.asInstanceOf[Ptr[Byte]]
object types:
export _root_.libtest.structs.*
object all:
export _root_.libtest.structs.Arr
Global enums are rendered as constants
Source C
code
enum {
HELLO = 25,
BYEBYE = 11
};
enum {
HOW=-1,
DOESTHIS=-2,
WORK=0
};
Generated Scala
code
package libtest
import _root_.scala.scalanative.unsafe.*
import _root_.scala.scalanative.unsigned.*
import _root_.scala.scalanative.libc.*
import _root_.scala.scalanative.*
object constants:
val HELLO: CUnsignedInt = 25.toUInt
val BYEBYE: CUnsignedInt = 11.toUInt
val HOW: CInt = -1
val DOESTHIS: CInt = -2
val WORK: CInt = 0