-
Notifications
You must be signed in to change notification settings - Fork 6
C Interoperability
VSL is fully interoperable with C functions. VSL doesn't use function-prototypes instead, you can declare a function 'external', as in having its body externally defined. Ensure that parameters and return type are correct (even if some parameters are not used) as you may encounter runtime errors otherwise.
func puts(cstring: CString) external(puts);
Note: Pointers are very unsafe. Any error thrown by pointer faults (e.g. SIGSEGV) cannot be gracefully handled by VSL. For these errors your best bet is to use LLDB to identify which VSL frame throws the error but do note with inlining and function splitting this might be difficult unless optimization is disabled. If the error disappears when optimization disabled, it is likely a concurrency issue.
Pointers are a special class which are not objects and seamlessly compile to C-pointers. This said, they do use generic-syntax, specifically to specify a pointer of a type T
, you use Pointer<T>
. An example of a pointer to an integer:
let intPtr: Pointer<Int>
The Pointer
class can be subclassed however it does not support dynamic dispatch by design. To obtain a pointer to an existing value, Pointer
offers a constructor, Pointer<T>(to: T)
:
let intPtr = Pointer<Int>(to: 1)
What init(to:)
will do is heap-allocate the memory for a single Int
and then will copy 1
to the newly allocated portion of memory. If the parameter is a non-heap allocated struct then the whole struct would be copied. This behavior ensures init(to:)
will never error. You may also use init(toUnsafe:)
this will error when obtaining the pointer to a stack-allocated or register value however it therefore also lacks the overhead.
Using init(to:)
with an object will generate a pointer to the object which is already a pointer:
let animalPtr = Pointer<Animal>(to: animal)
animal === Animal::animalPtr.dereference()
Additionally there is an init(of:)
which will interpret the pointer value of a given value. An example:
let animalPtr = Pointer<Animal>(of: animal)
assert(animal === Animal::animalPtr) // true
Do note that if you do something such as Pointer<Int>(of: 0)
this will essentially be the null pointer, doing Pointer<Int>(of: 0).dereference()
would generally result in a segmentation fault.
Pointers are unsafe and you must explicitly manage the memory of the contained objects. Additionally, if you used an externally managed reference, be aware that VSL may clean the referenced section of memory. To avoid, this, you may want to unqualified the variable from automatic memory management:
@unmanaged let myAnimal = Animal()
let int = Pointer<Animal>(to: 1)
In the above case the @unmanaged
annotation is redundant however it may be necessary in more complex cases. Pointers do not qualify for automatic memory management.
You can manage memory allocations using Pointer<T>(alloc: Int)
and Pointer<T>#free()
. Generally these functions will use the system malloc
/free
.
Tip: If you wish to interact with VSL types from a C-library see the vsl/native.h subsection
To allocate a sequence of memory use:
Pointer<T>(alloc count: UInt)
The alloc count
parameter determines the amount of consecutive spots in memory of size T
should be allocated. Roughly equivalent to the C call:
(T*) malloc(sizeof(T) * count)
To free the returned reference, use:
myPointer.free()
Warning: If
myPointer
does not refer to a section of memory allocated using VSL, expect runtime errors. Managing memory is an unsafe operation and VSL assumes you know what you are doing when using pointer types.
In C you often denote byte sequences using char*
(for strings). VSL has a byte-sequence literal. All characters inside the byte-sequence literal are literally inserted inside the binary and you can obtain a pointer to this sequence. The length is not stored and you must manually specify a null-terminator if appropriate. The syntax for a byte-sequence is:
puts(`Hello, World!\x0A\x00`)
\x00
is an example of the two hex-digit escape codes you can use. Hex escape values must be between 0 and FF inclusive otherwise the sequence will be literally inserted. It may initially be thought that Pointer<UInt8>
is the correct data type for the byte sequence however the Pointer
type is semantically unrelated to the byte-sequence. A byte-sequence literal is of type ByteSequence
. This class has no properties or methods. To interact with the byte-sequence, cast it to a Pointer
using bit-casts:
let byteSequence: ByteSequence = `Hello, World!\x0A\x00`
let pointer = Pointer<UInt8>::byteSequence
print("First character byte sequence is #{pointer.dereference()}")
There is no difference if an external C-function is declared with Pointer<UInt8>
or ByteSequence
however these will not be implicitly converted. It is recommended that external functions accepting strings have ByteSequence
used as the param type.
- Introduction
- Installation
- VSL By Example
- Usage
- WASM (WebAssembly)
- The Basics
- Your First Program
- Syntax
- Concepts
- Modules
- Advanced Details
- Interop
- VSL Development
- Common Errors