C interop, 4 of 7
Structs with @CLayout
Lay a Struct out as C does, pass it to C by value and by pointer, and measure it.
What you’ll build
Two C functions that give back a struct, div and ldiv, whose results are Structs that match
C’s, and a Header Struct with a fixed array in it, which you measure and hand to C to clear.
A Struct C can see
@CLayout lays a Struct out exactly as C would: each field at its own alignment after the one
before, and the whole padded to a multiple of its strictest alignment, the largest of its fields’
alignments. C’s div gives back a div_t,
which is two ints:
Import std.core
@CLayout Struct DivResult
Field Quotient: Int32
Field Remainder: Int32
End Struct
Extern "C"
Function div(numerator: Int32, denominator: Int32): DivResult
End Extern
Local r = div(17, 5)
Print("17 / 5 is {r.Quotient} remainder {r.Remainder}")17 / 5 is 3 remainder 2
The fields’ names don’t need to match C’s; their order and types do. The Struct keeps everything
a Struct has, its constructor, ==, and Methods among them.
A Struct that an Extern Function takes or gives, by value, by ByRef, or through a Ptr, has to
be @CLayout. Without it, the declaration is an error:
Import std.core
Struct DivResult
Field Quotient: Int32
Field Remainder: Int32
End Struct
Extern "C"
Function div(numerator: Int32, denominator: Int32): DivResult
End Extern
Print(div(17, 5).Quotient)Compiling main.fb main.fb:9:57: error: C code sees DivResult here, so it has to be @CLayout, which lays it out as C does | 9 | Function div(numerator: Int32, denominator: Int32): DivResult | ^^^^^^^^^ error: couldn't build main.fb because of 1 error
Sizes that differ by OS
C’s long is 4 bytes on Windows and 8 on Linux and macOS. CLong is whichever the target has,
so a Struct with one matches C’s on each.
Import std.core
@CLayout Struct LongDivResult
Field Quotient: CLong
Field Remainder: CLong
End Struct
Extern "C"
Function ldiv(numerator: CLong, denominator: CLong): LongDivResult
End Extern
Local big = ldiv(1000000, 7)
Print(big.Quotient, big.Remainder)142857 1
What a @CLayout Struct holds
Its fields are what C has: numbers, Bool, Enums, CString, Ptrs, Callbacks, other @CLayout
Structs, and fixed arrays of these. A String, an array, or a Class’s object isn’t C data, and is
an error that lists what’s allowed. SizeOf<T>() gives the size C would.
Import std.core
Import std.ffi
@CLayout Struct Header
Field Kind: UInt8
Field Length: UInt32
Field Tag: UInt8[3]
End Struct
Extern "C"
Function memset(target: Ptr<Void>, value: Int32, count: UInt64): Ptr<Void>
End Extern
Print(SizeOf<Header>())
Local h = Header(1, 99, [65, 66, 67])
Unsafe
memset(AddressOf(h), 0, SizeOf<Header>() As UInt64)
End Unsafe
Print(h.Kind, h.Length, h.Tag)12 0 0 [0, 0, 0]
Header is 12 bytes: Kind at 0, three bytes of padding so that Length starts at 4, Tag at
8, and one byte more to make 12, a multiple of 4, Length’s alignment and the strictest of the
three. A fixed array aligns as its elements do, so Tag’s is 1. memset clears all of it through
the pointer AddressOf gives.
By value or by pointer
A Struct passed by value goes in registers or memory as the target’s C ABI says, as clang would
pass it, on every OS and processor FutureBlitz builds for. A C function that takes a pointer to a
struct takes a Ptr to the Struct, or the Struct as a ByRef parameter, which passes the
variable’s address with no Unsafe code. Declared that way, the same memset clears a Header
from safe code:
Import std.core
Import std.ffi
@CLayout Struct Header
Field Kind: UInt8
Field Length: UInt32
Field Tag: UInt8[3]
End Struct
Extern "C"
Function ClearHeader(ByRef target: Header, value: Int32, count: UInt64): Ptr<Void> = "memset"
End Extern
Local h = Header(1, 99, [65, 66, 67])
ClearHeader(ByRef h, 0, SizeOf<Header>() As UInt64)
Print(h.Kind, h.Length, h.Tag)0 0 [0, 0, 0]
What you learned
@CLayoutlays a Struct out as C does, and every Struct C code sees needs it.- Fields match C’s by order and type, not by name.
CLongkeeps a Struct right where C’slongdiffers by OS.SizeOf<T>()measures a Struct as C lays it out, padding included.
The rules are in the language reference.
Report an issue with this page or with FutureBlitz on GitHub.