This page describes a particular set of features for native code interoperability. These features are "language extensions", i.e. not compatible with F#.
WARN: These features will be removed in future, especially before v1 release.
It's assumed that you know about:
- Basics of the C language
- Undefined behavior (UB) in C
- Low-layer programming
Keywords that start with __
represent language extensions, which are unavailable in F#.
The compiler can't ensure these features to work correctly. If these features are used incorrectly at runtime, the program runs into undefined behavior (UB). Such program does anything weird with no error.
See Pointer Types.
FunPtr<T, U>
is a function pointer type. T
represents the parameter list and U
represents the result type.
T
is a tuple type or other:
T |
parameter list |
---|---|
unit |
() |
T (not tuple) |
(T) |
T1 * T2 |
(T1, T2) |
T1 * T2 * T3 |
(T1, T2, T3) |
U
is unit or other:
U |
result type |
---|---|
unit |
void |
U (not unit) |
U |
open Std.Ptr
// void(*)(void)
type ActionFun = FunPtr<unit, unit>
// int(*)(int)
type IntUnaryFun = FunPtr<int, int>
// int(*)(int, int)
type IntBinaryFun = FunPtr<int * int, int>
- Value of
FunPtr
shouldn't be null. - Calling convention is same as C.
&&f
represents a function pointer of a function f
, where f
is a non-local function defined by let-fun syntax.
open Std.Ptr
let f (x: int) : int = x + 1
let fp: FunPtr<int, int> = &&f
Pointer to local functions can't be taken since local functions might capture variables.
The FunPtr.invoke
primitive invokes a function pointer.
Unless it's 1-arity, it takes arguments as a tuple.
open Std.Ptr
// Calling to zero-arity function pointer.
FunPtr.invoke funPtr ()
// Calling to 1-arity function pointer.
FunPtr.invoke funPtr arg
// Calling to 2+-arity function pointer.
FunPtr.invoke funPtr (arg1, arg2, ...)
__nativeFun ("name", args...)
is a special expression to call a native function with the specified name.
An extern declaration of the function is also generated.
// void abort(void);
// abort();
__nativeFun "abort"
// void *calloc(size_t, size_t);
// void *p = calloc(4, sizeof(int));
let p: voidptr = __nativeFun ("calloc", 4un, unativeint sizeof<int>)
// ...
Called function must be linked statically.
Otherwise, link error will occur.
For example, if a program calls sqrt
function then it needs to link libm
.
(Just including <math.h>
isn't enough.)
Use manifest file to specify linker options (TODO: write document of manifest file!).
Restriction: Variadic parameter functions (e.g. printf
) can't be called with this syntax.
(Not implemented yet. Use dlopen
on Linux and link libdl
(-ldl
option). Use LoadLibrary
on Windows.)
sizeof<'T> : int
sizeof<'T>
is the size of type T in bytes. Equivalent to sizeof(T)
in C.
Opaque type is a kind of user-defined types.
[<Opaque>]
type Opaque = private | Opaque
The syntax is same as new-type discriminated union types. The variant won't be used.
Opaque types compile to struct declarations without definitions in C:
struct Opaque;
Incomplete struct types are commonly used as abstract data types and in object-oriented API.
Opaque types don't have definitions and you need to use it with some indirection such as nativeptr
.
__nativeExpr ("expression", arg1, arg2, ...)
is an expression to embed a C expression into generated code.
The string literal "expression"
contains an arbitrary C expression.
Other arguments are bound to placeholders (see below).
// int e = errno;
// (Note `errno` is a global variable in C.)
let e: int = __nativeExpr "errno"
__nativeExpr
can be arbitrary type, i.e. 'A'
. Incorrect type would incur C compile error. Warning: Result type must NOT be unit
; otherwise the expression might be erased during compilation.
__nativeExpr
should be used when the expression is pure. Use __nativeStmt
for side-effect.
Placeholder {i}
(i >= 0
) in the template is each replaced with the i'th placeholder argument.
Placeholder argument is compiled to C normally and substitutes a placeholder in the template.
// int z = x + y;
let z: int = __nativeExpr("{0} + {1}", x, y)
__type: T
is a special expression for placeholder argument.
It represents a type rather than value.
// size_t n = sizeof(struct String);
let n: unativeint = __nativeExpr ("sizeof({0})", (__type: string))
__nativeStmt ("statement", args...)
is an expression to embed a C statement into generated code.
The string literal "statement"
contains arbitrary C statement.
Other arguments are bound to placeholders (same as __nativeExpr
.)
__nativeStmt "abort();"
// printfn("%d", 42);
__nativeStmt """printfn("%d\n", 42);"""
__nativeDecl ("declaration", args...)
is an expression to embed a C declaration into generated code.
The string literal "declaration"
contains arbitrary C declaration.
Other arguments are bound to placeholders (same as __nativeExpr
.)
Declarations are hoisted to top-level (even if __nativeDecl
is used inside a function.)
__nativeDecl "#include <errno.h>"
Value of __nativeDecl (...)
is ()
.
Placeholder arguments are limited to expressions any of:
- Literals,
- Names,
&&f
(function pointer), or(__type: 'T)
;
since arguments must be evaluated without using statements.
__nativeType<T>
is a special type to embed a C type into generated code.
T
is an identifier, which can be undefined.
type F = __nativeType<FILE>
// FILE *input = stdin;
let input: nativeptr<F> = __nativeExpr "stdin"