- Antril
- Referencia
- Interoperabilidad con C
Esta página aún no está traducida al español. Leer la versión en inglés
Interop with C
Calling C libraries with [Import], exporting functions with [Export], strings that cross, func* function pointers, structs and unions with [ValueLayout], and native memory with Marshal.
En esta página
Antril calls C functions in native libraries (.dll, .so), and C can call Antril functions. What crosses is what C holds: numbers, bool, pointers, function pointers, and strings copied for the call. Most of it uses pointers, so it lives in unsafe code.
To make the declarations for a whole C library from its headers, use larva bind instead of writing them by hand.
Importing a C function
[Import("library")] on a function without a body declares a C function of that library. The C name is the function's name, or the second argument: [Import("kernel32.dll", "lstrlenW")].
import System;
import System.Runtime.InteropServices;
[Import("kernel32.dll")] GetTickCount64() : u64;
[Import("kernel32.dll", "lstrlenW")] WideLength(String<u16> text) : i32;
[Import("kernel32.dll", "lstrlenA")] Utf8Length(str text) : i32;
[Import("ucrtbase.dll")] qsort(void* items, u64 count, u64 size, func*<void*, void*> : i32 compare);
[Import("ucrtbase.dll")] getenv(str name) : u8*;
unsafe Compare(void* a, void* b) : i32 => *(i32*)a - *(i32*)b;
Main()
{
Console.WriteLine(GetTickCount64() > 0);
Console.WriteLine(WideLength(new String<u16>(Unicode.ToUtf16("año"))));
Console.WriteLine(Utf8Length("año"));
i32[] data = [4, 1, 3, 2];
unsafe
{
qsort(&data[0], 4, 4, &Compare);
u8* value = getenv("OS");
Console.WriteLine(Marshal.PtrToStringUTF8(value));
}
Console.WriteLine(data[0] + "," + data[1] + "," + data[2] + "," + data[3]);
}
On Windows:
larva run
true
3
4
Windows_NT
1,2,3,4
- The library is loaded, and the function found, the first time it is called: there is nothing to link.
- An imported function is static, has no body and no type parameters, and is not virtual (AN1051). It names its library (AN1052).
- Imported functions can be free functions or members of a type, and can be called from safe code; only pointer arguments need an unsafe block.
Import and Export are read by the compiler by name, so they need no import. System.Runtime.InteropServices holds Marshal, NativeMemory and the layout attributes.
What crosses
| Antril | C | As a parameter | As a result |
|---|---|---|---|
i8 … i64, u8 … u64, f32, f64, iptr, uptr |
the same width | yes | yes |
bool |
a one-byte bool | yes | yes |
T*, void* |
a pointer | yes | yes |
func*<A> : R |
a function pointer | yes | yes |
str |
const char*: a temporary null-terminated UTF-8 copy |
yes | no |
String<u16> |
const wchar_t* on Windows: a temporary null-terminated UTF-16 copy |
yes | no |
str?, String<u16>? |
the same, or NULL for null |
yes | no |
Anything else is an error: objects, arrays and value types as parameters (AN1053) or results (AN1054). The string copies live only during the call; C must not keep them. A string C returns comes back as a u8* (or u16*), read with Marshal.PtrToStringUTF8 or Marshal.PtrToStringUni.
str is UTF-8 text. String<u16> is UTF-16 text: make one from a str with new String<u16>(Unicode.ToUtf16(text)).
Structs
A C struct is a value with the same fields, in the same order. Antril lays out a value as C does by default (in order, each field at its natural alignment), so it crosses by pointer as it is:
import System;
public value SystemTime
{
public:
u16 Year;
u16 Month;
u16 DayOfWeek;
u16 Day;
u16 Hour;
u16 Minute;
u16 Second;
u16 Milliseconds;
}
[Import("kernel32.dll")] GetSystemTime(SystemTime* time);
Main()
{
SystemTime now;
unsafe { GetSystemTime(&now); }
Console.WriteLine(now.Year > 2000);
}
larva run
true
A struct crosses only by pointer. By value it can only be a field of another struct.
Warning
Not yet available: passing or returning a struct by value to or from C.
Layouts
[ValueLayout] and [FieldOffset] (in System.Runtime.InteropServices) say where each field goes, for structs and unions that need another layout:
| Attribute | What it does |
|---|---|
[ValueLayout(Layout.Sequential)] |
Fields in order, each at its natural alignment: the default layout, written out. |
[ValueLayout(Layout.Sequential, Pack = n)] |
The same, with no field aligned to more than n bytes (0, 1, 2, 4, 8 or 16; 0 means each field's own). |
[ValueLayout(Layout.Explicit)] |
Each field where its [FieldOffset(n)] says, in bytes from the start. Fields may overlap: a C union has every field at offset 0. |
Size = n |
Makes the value at least n bytes. |
import System;
import System.Runtime.InteropServices;
[ValueLayout(Layout.Explicit)]
public value Number
{
public:
[FieldOffset(0)] i64 Whole;
[FieldOffset(0)] f64 Real;
[FieldOffset(4)] u32 High;
}
[ValueLayout(Layout.Sequential, Pack = 1)]
public value Packed
{
public:
u8 Tag;
u32 Length;
fixed u8[3] Name;
}
[ValueLayout(Layout.Sequential, Size = 32)]
public value Padded
{
public:
i32 A;
}
public value Natural
{
public:
u8 Tag;
u32 Length;
}
Main()
{
Number n;
n.Real = 1.0;
Console.WriteLine(n.High);
Console.WriteLine(sizeof(Number));
Console.WriteLine(sizeof(Packed));
Console.WriteLine(sizeof(Padded));
Console.WriteLine(sizeof(Natural));
}
larva run
1072693248
8
8
32
8
n.High reads the upper half of the bits of 1.0. Such a value is copied, passed by pointer, nested and zeroed like any other.
| Code | When |
|---|---|
| AN1080 | [ValueLayout] on something other than a value without type parameters. |
| AN1081 | A field of a laid-out value holds objects. Fields are numbers, bools, pointers, func*, fixed buffers and other such values. |
| AN1082 | [FieldOffset] without Layout.Explicit, or a field of an explicit value without one. |
| AN1083 | Pack is not 0, 1, 2, 4, 8 or 16. |
Function pointers
func*<A, B> : R is the address of a function that C can call: no captured object, the C calling convention, and only C types as parameters and result (AN1057). Without : R it returns nothing.
&Fgives the address of a static, non-generic functionF; whenFis overloaded, the expectedfunc*type picks the overload (AN2118 when none fits). Its parameters and result must be C types (AN2119).- Calling a
func*runs code at an address, so it goes in unsafe code (AN2120).
import System;
Scale(i32 x, i32 y) : i32 => x * y + 1;
Main()
{
unsafe
{
func*<i32, i32> : i32 scale = &Scale;
Console.WriteLine(scale(6, 7));
}
}
larva run
43
The qsort call above passes &Compare to C, which calls it back.
Exporting a function
[Export] or [Export("name")] on a static function with a body makes it a C symbol of the program or library, under its own name or the one given. It takes and gives only C types: numbers, bool, pointers and func* (AN1056); it has no type parameters and is not virtual (AN1055).
[Export("antril_scale")]
Scale(i32 x, i32 y) : i32 => x * y + 1;
Main()
{
}
Native memory
System.Runtime.InteropServices has what moves data between Antril and C memory. All of it is unsafe.
| Member | What it does |
|---|---|
Marshal.AllocHGlobal(bytes), Marshal.FreeHGlobal(p) |
Zeroed native memory, and giving it back. |
Marshal.AllocCoTaskMem(bytes), Marshal.FreeCoTaskMem(p) |
The same. |
Marshal.StringToHGlobalUTF8(text) |
A null-terminated UTF-8 copy of text in native memory (a u8*). |
Marshal.StringToHGlobalUni(text) |
A null-terminated UTF-16 copy (a u16*). |
Marshal.PtrToStringUTF8(p), Marshal.PtrToStringUTF8(p, length) |
The UTF-8 text at p (null-terminated, or length bytes) as a str; null gives "". |
Marshal.PtrToStringUni(p), Marshal.PtrToStringUni(p, length) |
The same for UTF-16. |
Marshal.ReadByte, ReadInt16, ReadInt32, ReadInt64, ReadIntPtr |
Reads at an address, with an optional byte offset. |
Marshal.WriteByte, WriteInt16, WriteInt32, WriteInt64, WriteIntPtr |
Writes at an address, with an optional byte offset. |
Marshal.Copy(array, start, p, length), Marshal.Copy(p, array, start, length) |
Copies numbers between an array and native memory. |
Marshal.SizeOf<T>() |
The size of a T in bytes. |
NativeMemory.Alloc(bytes), AllocZeroed(count, size), Free(p), Copy(source, destination, bytes) |
Native blocks. |
Memory.Copy(target, source, count), Memory.Compare(left, right, count) |
Copies or compares bytes. |
Native memory is not seen by the collector: give back what you take.
Next: Attributes.
Editar esta página en GitHub Reportar un problema