Function sets are groupings of functions in Slinc. Each can be configured in a number of ways.
A function set is a Scala trait
that derives the FSet
type class. Method declarations within it are used as a template for the bindings to C that the Slinc runtime will generate.
FSet declarations
FSet declarations are declarations of C library bindings without any entanglement in runtime details. This means that an fset declaration only requires the use of the Slinc core library, and does not effect the runtime requirements of users of the module.
FSet naming
The type name of the module does not matter to Slinc.
Method naming
The name of method declarations within an fset module should reflect the name of the C function you wish to bind to. If a method name is not found in the C namespace, this will cause a runtime error when you try to summon the module implementation.
Example:
trait L derives FSet:
def abs(i: CInt): CInt
This library module L
has a binding to abs
in the C standard library namespace.
Naming overrides
You can override the C name lookup for a method declaration on a host dependent basis with the @NameOverride
annotation. You provide the @NameOverride
annotation with the alternative name, and an OS and architecture tuple where the name should be used. An example follows:
trait L derives Lib:
@NameOverride("_time64", OS.Windows -> Arch.X64)
def time(timer: Ptr[TimeT]): TimeT
This function is helpful when you have a function symbol that should be present on a platform, but has an alternative name for some reason. In the case with Windows, the time
function in the C standard library is a macro that points to _time64
on 64-bit platforms, and _time32
on 32-bit platforms. Since macros do not exist as symbols in the C standard library namespace, this NameOverride
makes the Slinc platform choose the right function name on Windows X64.
Dependencies
FSets can be declared that depend on C libraries in a number of different ways:
- libraries stored in resources
- libraries at a relative location compared to the program
- libraries on the system path
- libraries at an absolute location
- C files located in resources
Libraries in resources
A library can be stored in the jar for the program, and an FSet can depend on it with the @NeedsResource
annotation.
import fr.hammons.slinc.annotations.*
import fr.hammons.slinc.types.*
import fr.hammons.slinc.*
@NeedsResource("my_lib.so")
trait L derives FSet:
def my_fun(i: CInt): Unit
This indicates that the FSet named L
needs to load /native/my_lib.so
from the jar resources. This dependency declaration is rather platform specific, since it depends on a specific .so file.
Platform dependent resource dependencies
If you just provide the name of the library, not a specific .so file, Slinc will look in the jar resources for a library with that kind of name but with a library suffix and architecture tag based on the platform the program is run on. For example:
import fr.hammons.slinc.annotations.*
import fr.hammons.slinc.types.*
import fr.hammons.slinc.*
@NeedsResource("my_lib")
trait L derives FSet:
def my_fun(i: CInt): Unit
The slinc runtime will look for /native/my_lib_x64.so
on x86_64 linux, and /native/my_lib_x64.dll
on x86_64 Windows.
Libraries on the system path
If the library is on the system path that the JVM is aware of, you can declare an FSet's dependence on it with the Needs
annotation.
import fr.hammons.slinc.annotations.*
import fr.hammons.slinc.types.*
import fr.hammons.slinc.*
@Needs("z")
trait L derives FSet:
def zlibVersion(): Ptr[CChar]
This declaration is for a binding to zlib.
Libraries in the filesystem
If the library dependency is located on the filesystem, you can use an absolute or relative path with the @NeedsFile
annotation.
import fr.hammons.slinc.annotations.*
import fr.hammons.slinc.types.*
import fr.hammons.slinc.*
//relative path
@NeedsFile("my_lib.so")
trait A derives FSet:
def my_fn(): Unit
//absolute path
@NeedsFile("/tmp/my_lib.so")
trait B derives FSet:
def my_fn(): Unit
Platform dependent filesystem dependencies
If you do not provide an absolute file name (file ending with .so or .dll), Slinc will use the base file name provided along with the appropriate library suffix for the OS and a tag based on the architecture.
For example, on Windows x86_64, @NeedsFile("my_lib")
will look for .\my_lib_x64.dll
.
The architecture tags for the architectures follows:
arch | tags |
---|---|
x86_64 | x64, amd64, x86_64 |
C file jar resources
If a C file is placed in a jar, under the /native
you can use the @NeedsResource
annotation to have Slinc compile and load the file at runtime. In order for this to work, one must have clang installed on the target system.
import fr.hammons.slinc.annotations.*
import fr.hammons.slinc.types.*
import fr.hammons.slinc.*
@NeedsResource("my_lib.c")
trait L derives FSet:
def my_fn(): Unit
Summoning FSet Implementations
FSet declarations have been shown above, but they are not useable without being summoned. Doing so requires the Slinc runtime on your classpath, and makes the JAR and class files generated dependent on a specific JVM.
To summon an fset implementation, you use the FSet.instance[?]
method as shown in the following example:
import fr.hammons.slinc.types.CInt
import fr.hammons.slinc.FSet
import fr.hammons.slinc.runtime.given
trait L derives FSet:
def abs(i: CInt): CInt
val l = FSet.instance[L]
@main def program = println(l.abs(4))
Note the assignment of the instance to a val
. This is not strictly necessary, and FSet.instance
will always return the same module instance, but re-summoning is more expensive than storing the summoned module implementation.