mirror of
https://github.com/openresty/openresty
synced 2024-11-09 18:35:51 +01:00
1386 lines
40 KiB
C
1386 lines
40 KiB
C
=pod
|
|
|
|
LuaJIT
|
|
|
|
=head1 FFI Semantics
|
|
|
|
=over
|
|
|
|
=item * LuaJIT
|
|
|
|
=over
|
|
|
|
=item * Download E<rchevron>
|
|
|
|
=item * Installation
|
|
|
|
=item * Running
|
|
|
|
=back
|
|
|
|
=item * Extensions
|
|
|
|
=over
|
|
|
|
=item * FFI Library
|
|
|
|
=over
|
|
|
|
=item * FFI Tutorial
|
|
|
|
=item * ffi.* API
|
|
|
|
=item * FFI Semantics
|
|
|
|
=back
|
|
|
|
=item * jit.* Library
|
|
|
|
=item * Lua/C API
|
|
|
|
=item * Profiler
|
|
|
|
=back
|
|
|
|
=item * Status
|
|
|
|
=over
|
|
|
|
=item * Changes
|
|
|
|
=back
|
|
|
|
=item * FAQ
|
|
|
|
=item * Performance E<rchevron>
|
|
|
|
=item * Wiki E<rchevron>
|
|
|
|
=item * Mailing List E<rchevron>
|
|
|
|
=back
|
|
|
|
This page describes the detailed semantics underlying the FFI library
|
|
and its interaction with both Lua and C code.
|
|
|
|
Given that the FFI library is designed to interface with C code and
|
|
that declarations can be written in plain C syntax, B<it closely
|
|
follows the C language semantics>, wherever possible. Some minor
|
|
concessions are needed for smoother interoperation with Lua language
|
|
semantics.
|
|
|
|
Please don't be overwhelmed by the contents of this page E<mdash> this
|
|
is a reference and you may need to consult it, if in doubt. It doesn't
|
|
hurt to skim this page, but most of the semantics "just work" as you'd
|
|
expect them to work. It should be straightforward to write applications
|
|
using the LuaJIT FFI for developers with a C or C++ background.
|
|
|
|
=head2 C Language Support
|
|
|
|
The FFI library has a built-in C parser with a minimal memory
|
|
footprint. It's used by the ffi.* library functions to declare C types
|
|
or external symbols.
|
|
|
|
It's only purpose is to parse C declarations, as found e.g. in C header
|
|
files. Although it does evaluate constant expressions, it's I<not> a C
|
|
compiler. The body of C<inline> C function definitions is simply
|
|
ignored.
|
|
|
|
Also, this is I<not> a validating C parser. It expects and accepts
|
|
correctly formed C declarations, but it may choose to ignore bad
|
|
declarations or show rather generic error messages. If in doubt, please
|
|
check the input against your favorite C compiler.
|
|
|
|
The C parser complies to the B<C99 language standard> plus the
|
|
following extensions:
|
|
|
|
=over
|
|
|
|
=item * The C<'\e'> escape in character and string literals.
|
|
|
|
=item * The C99/C++ boolean type, declared with the keywords C<bool> or
|
|
C<_Bool>.
|
|
|
|
=item * Complex numbers, declared with the keywords C<complex> or
|
|
C<_Complex>.
|
|
|
|
=item * Two complex number types: C<complex> (aka C<complex double>)
|
|
and C<complex float>.
|
|
|
|
=item * Vector types, declared with the GCC C<mode> or C<vector_size>
|
|
attribute.
|
|
|
|
=item * Unnamed ('transparent') C<struct>/C<union> fields inside a
|
|
C<struct>/C<union>.
|
|
|
|
=item * Incomplete C<enum> declarations, handled like incomplete
|
|
C<struct> declarations.
|
|
|
|
=item * Unnamed C<enum> fields inside a C<struct>/C<union>. This is
|
|
similar to a scoped C++ C<enum>, except that declared constants are
|
|
visible in the global namespace, too.
|
|
|
|
=item * Scoped C<static const> declarations inside a C<struct>/C<union>
|
|
(from C++).
|
|
|
|
=item * Zero-length arrays (C<[0]>), empty C<struct>/C<union>,
|
|
variable-length arrays (VLA, C<[?]>) and variable-length structs (VLS,
|
|
with a trailing VLA).
|
|
|
|
=item * C++ reference types (C<int &x>).
|
|
|
|
=item * Alternate GCC keywords with 'C<__>', e.g. C<__const__>.
|
|
|
|
=item * GCC C<__attribute__> with the following attributes: C<aligned>,
|
|
C<packed>, C<mode>, C<vector_size>, C<cdecl>, C<fastcall>, C<stdcall>,
|
|
C<thiscall>.
|
|
|
|
=item * The GCC C<__extension__> keyword and the GCC C<__alignof__>
|
|
operator.
|
|
|
|
=item * GCC C<__asm__("symname")> symbol name redirection for function
|
|
declarations.
|
|
|
|
=item * MSVC keywords for fixed-length types: C<__int8>, C<__int16>,
|
|
C<__int32> and C<__int64>.
|
|
|
|
=item * MSVC C<__cdecl>, C<__fastcall>, C<__stdcall>, C<__thiscall>,
|
|
C<__ptr32>, C<__ptr64>, C<__declspec(align(n))> and C<#pragma pack>.
|
|
|
|
=item * All other GCC/MSVC-specific attributes are ignored.
|
|
|
|
=back
|
|
|
|
The following C types are pre-defined by the C parser (like a
|
|
C<typedef>, except re-declarations will be ignored):
|
|
|
|
=over
|
|
|
|
=item * Vararg handling: C<va_list>, C<__builtin_va_list>,
|
|
C<__gnuc_va_list>.
|
|
|
|
=item * From C<E<lt>stddef.hE<gt>>: C<ptrdiff_t>, C<size_t>,
|
|
C<wchar_t>.
|
|
|
|
=item * From C<E<lt>stdint.hE<gt>>: C<int8_t>, C<int16_t>, C<int32_t>,
|
|
C<int64_t>, C<uint8_t>, C<uint16_t>, C<uint32_t>, C<uint64_t>,
|
|
C<intptr_t>, C<uintptr_t>.
|
|
|
|
=item * From C<E<lt>unistd.hE<gt>> (POSIX): C<ssize_t>.
|
|
|
|
=back
|
|
|
|
You're encouraged to use these types in preference to compiler-specific
|
|
extensions or target-dependent standard types. E.g. C<char> differs in
|
|
signedness and C<long> differs in size, depending on the target
|
|
architecture and platform ABI.
|
|
|
|
The following C features are B<not> supported:
|
|
|
|
=over
|
|
|
|
=item * A declaration must always have a type specifier; it doesn't
|
|
default to an C<int> type.
|
|
|
|
=item * Old-style empty function declarations (K&R) are not allowed.
|
|
All C functions must have a proper prototype declaration. A function
|
|
declared without parameters (C<int foo();>) is treated as a function
|
|
taking zero arguments, like in C++.
|
|
|
|
=item * The C<long double> C type is parsed correctly, but there's no
|
|
support for the related conversions, accesses or arithmetic operations.
|
|
|
|
=item * Wide character strings and character literals are not
|
|
supported.
|
|
|
|
=item * See below for features that are currently not implemented.
|
|
|
|
=back
|
|
|
|
=head2 C Type Conversion Rules
|
|
|
|
=head2 Conversions from C types to Lua objects
|
|
|
|
These conversion rules apply for I<read accesses> to C types: indexing
|
|
pointers, arrays or C<struct>/C<union> types; reading external
|
|
variables or constant values; retrieving return values from C calls:
|
|
|
|
Input
|
|
|
|
Conversion
|
|
|
|
Output
|
|
|
|
C<int8_t>, C<int16_t>
|
|
|
|
E<rarr>sign-ext C<int32_t> E<rarr> C<double>
|
|
|
|
number
|
|
|
|
C<uint8_t>, C<uint16_t>
|
|
|
|
E<rarr>zero-ext C<int32_t> E<rarr> C<double>
|
|
|
|
number
|
|
|
|
C<int32_t>, C<uint32_t>
|
|
|
|
E<rarr> C<double>
|
|
|
|
number
|
|
|
|
C<int64_t>, C<uint64_t>
|
|
|
|
boxed value
|
|
|
|
64 bit int cdata
|
|
|
|
C<double>, C<float>
|
|
|
|
E<rarr> C<double>
|
|
|
|
number
|
|
|
|
C<bool>
|
|
|
|
0 E<rarr> C<false>, otherwise C<true>
|
|
|
|
boolean
|
|
|
|
C<enum>
|
|
|
|
boxed value
|
|
|
|
enum cdata
|
|
|
|
Complex number
|
|
|
|
boxed value
|
|
|
|
complex cdata
|
|
|
|
Vector
|
|
|
|
boxed value
|
|
|
|
vector cdata
|
|
|
|
Pointer
|
|
|
|
boxed value
|
|
|
|
pointer cdata
|
|
|
|
Array
|
|
|
|
boxed reference
|
|
|
|
reference cdata
|
|
|
|
C<struct>/C<union>
|
|
|
|
boxed reference
|
|
|
|
reference cdata
|
|
|
|
Bitfields are treated like their underlying type.
|
|
|
|
Reference types are dereferenced I<before> a conversion can take place
|
|
E<mdash> the conversion is applied to the C type pointed to by the
|
|
reference.
|
|
|
|
=head2 Conversions from Lua objects to C types
|
|
|
|
These conversion rules apply for I<write accesses> to C types: indexing
|
|
pointers, arrays or C<struct>/C<union> types; initializing cdata
|
|
objects; casts to C types; writing to external variables; passing
|
|
arguments to C calls:
|
|
|
|
Input
|
|
|
|
Conversion
|
|
|
|
Output
|
|
|
|
number
|
|
|
|
E<rarr>
|
|
|
|
C<double>
|
|
|
|
boolean
|
|
|
|
C<false> E<rarr> 0, C<true> E<rarr> 1
|
|
|
|
C<bool>
|
|
|
|
nil
|
|
|
|
C<NULL> E<rarr>
|
|
|
|
C<(void *)>
|
|
|
|
lightuserdata
|
|
|
|
lightuserdata address E<rarr>
|
|
|
|
C<(void *)>
|
|
|
|
userdata
|
|
|
|
userdata payload E<rarr>
|
|
|
|
C<(void *)>
|
|
|
|
io.* file
|
|
|
|
get FILE * handle E<rarr>
|
|
|
|
C<(void *)>
|
|
|
|
string
|
|
|
|
match against C<enum> constant
|
|
|
|
C<enum>
|
|
|
|
string
|
|
|
|
copy string data + zero-byte
|
|
|
|
C<int8_t[]>, C<uint8_t[]>
|
|
|
|
string
|
|
|
|
string data E<rarr>
|
|
|
|
C<const char[]>
|
|
|
|
function
|
|
|
|
create callback E<rarr>
|
|
|
|
C function type
|
|
|
|
table
|
|
|
|
table initializer
|
|
|
|
Array
|
|
|
|
table
|
|
|
|
table initializer
|
|
|
|
C<struct>/C<union>
|
|
|
|
cdata
|
|
|
|
cdata payload E<rarr>
|
|
|
|
C type
|
|
|
|
If the result type of this conversion doesn't match the C type of the
|
|
destination, the conversion rules between C types are applied.
|
|
|
|
Reference types are immutable after initialization ("no re-seating of
|
|
references"). For initialization purposes or when passing values to
|
|
reference parameters, they are treated like pointers. Note that unlike
|
|
in C++, there's no way to implement automatic reference generation of
|
|
variables under the Lua language semantics. If you want to call a
|
|
function with a reference parameter, you need to explicitly pass a
|
|
one-element array.
|
|
|
|
=head2 Conversions between C types
|
|
|
|
These conversion rules are more or less the same as the standard C
|
|
conversion rules. Some rules only apply to casts, or require pointer or
|
|
type compatibility:
|
|
|
|
Input
|
|
|
|
Conversion
|
|
|
|
Output
|
|
|
|
Signed integer
|
|
|
|
E<rarr>narrow or sign-extend
|
|
|
|
Integer
|
|
|
|
Unsigned integer
|
|
|
|
E<rarr>narrow or zero-extend
|
|
|
|
Integer
|
|
|
|
Integer
|
|
|
|
E<rarr>round
|
|
|
|
C<double>, C<float>
|
|
|
|
C<double>, C<float>
|
|
|
|
E<rarr>trunc C<int32_t> E<rarr>narrow
|
|
|
|
C<(u)int8_t>, C<(u)int16_t>
|
|
|
|
C<double>, C<float>
|
|
|
|
E<rarr>trunc
|
|
|
|
C<(u)int32_t>, C<(u)int64_t>
|
|
|
|
C<double>, C<float>
|
|
|
|
E<rarr>round
|
|
|
|
C<float>, C<double>
|
|
|
|
Number
|
|
|
|
n == 0 E<rarr> 0, otherwise 1
|
|
|
|
C<bool>
|
|
|
|
C<bool>
|
|
|
|
C<false> E<rarr> 0, C<true> E<rarr> 1
|
|
|
|
Number
|
|
|
|
Complex number
|
|
|
|
convert real part
|
|
|
|
Number
|
|
|
|
Number
|
|
|
|
convert real part, imag = 0
|
|
|
|
Complex number
|
|
|
|
Complex number
|
|
|
|
convert real and imag part
|
|
|
|
Complex number
|
|
|
|
Number
|
|
|
|
convert scalar and replicate
|
|
|
|
Vector
|
|
|
|
Vector
|
|
|
|
copy (same size)
|
|
|
|
Vector
|
|
|
|
C<struct>/C<union>
|
|
|
|
take base address (compat)
|
|
|
|
Pointer
|
|
|
|
Array
|
|
|
|
take base address (compat)
|
|
|
|
Pointer
|
|
|
|
Function
|
|
|
|
take function address
|
|
|
|
Function pointer
|
|
|
|
Number
|
|
|
|
convert via C<uintptr_t> (cast)
|
|
|
|
Pointer
|
|
|
|
Pointer
|
|
|
|
convert address (compat/cast)
|
|
|
|
Pointer
|
|
|
|
Pointer
|
|
|
|
convert address (cast)
|
|
|
|
Integer
|
|
|
|
Array
|
|
|
|
convert base address (cast)
|
|
|
|
Integer
|
|
|
|
Array
|
|
|
|
copy (compat)
|
|
|
|
Array
|
|
|
|
C<struct>/C<union>
|
|
|
|
copy (identical type)
|
|
|
|
C<struct>/C<union>
|
|
|
|
Bitfields or C<enum> types are treated like their underlying type.
|
|
|
|
Conversions not listed above will raise an error. E.g. it's not
|
|
possible to convert a pointer to a complex number or vice versa.
|
|
|
|
=head2 Conversions for vararg C function arguments
|
|
|
|
The following default conversion rules apply when passing Lua objects
|
|
to the variable argument part of vararg C functions:
|
|
|
|
Input
|
|
|
|
Conversion
|
|
|
|
Output
|
|
|
|
number
|
|
|
|
E<rarr>
|
|
|
|
C<double>
|
|
|
|
boolean
|
|
|
|
C<false> E<rarr> 0, C<true> E<rarr> 1
|
|
|
|
C<bool>
|
|
|
|
nil
|
|
|
|
C<NULL> E<rarr>
|
|
|
|
C<(void *)>
|
|
|
|
userdata
|
|
|
|
userdata payload E<rarr>
|
|
|
|
C<(void *)>
|
|
|
|
lightuserdata
|
|
|
|
lightuserdata address E<rarr>
|
|
|
|
C<(void *)>
|
|
|
|
string
|
|
|
|
string data E<rarr>
|
|
|
|
C<const char *>
|
|
|
|
C<float> cdata
|
|
|
|
E<rarr>
|
|
|
|
C<double>
|
|
|
|
Array cdata
|
|
|
|
take base address
|
|
|
|
Element pointer
|
|
|
|
C<struct>/C<union> cdata
|
|
|
|
take base address
|
|
|
|
C<struct>/C<union> pointer
|
|
|
|
Function cdata
|
|
|
|
take function address
|
|
|
|
Function pointer
|
|
|
|
Any other cdata
|
|
|
|
no conversion
|
|
|
|
C type
|
|
|
|
To pass a Lua object, other than a cdata object, as a specific type,
|
|
you need to override the conversion rules: create a temporary cdata
|
|
object with a constructor or a cast and initialize it with the value to
|
|
pass:
|
|
|
|
Assuming C<x> is a Lua number, here's how to pass it as an integer to a
|
|
vararg function:
|
|
|
|
ffi.cdef[[
|
|
int printf(const char *fmt, ...);
|
|
]]
|
|
ffi.C.printf("integer value: %d\n", ffi.new("int", x))
|
|
|
|
If you don't do this, the default Lua number E<rarr> C<double>
|
|
conversion rule applies. A vararg C function expecting an integer will
|
|
see a garbled or uninitialized value.
|
|
|
|
=head2 Initializers
|
|
|
|
Creating a cdata object with C<ffi.new()> or the equivalent constructor
|
|
syntax always initializes its contents, too. Different rules apply,
|
|
depending on the number of optional initializers and the C types
|
|
involved:
|
|
|
|
=over
|
|
|
|
=item * If no initializers are given, the object is filled with zero
|
|
bytes.
|
|
|
|
=item * Scalar types (numbers and pointers) accept a single
|
|
initializer. The Lua object is converted to the scalar C type.
|
|
|
|
=item * Valarrays (complex numbers and vectors) are treated like
|
|
scalars when a single initializer is given. Otherwise they are treated
|
|
like regular arrays.
|
|
|
|
=item * Aggregate types (arrays and structs) accept either a single
|
|
cdata initializer of the same type (copy constructor), a single table
|
|
initializer, or a flat list of initializers.
|
|
|
|
=item * The elements of an array are initialized, starting at index
|
|
zero. If a single initializer is given for an array, it's repeated for
|
|
all remaining elements. This doesn't happen if two or more initializers
|
|
are given: all remaining uninitialized elements are filled with zero
|
|
bytes.
|
|
|
|
=item * Byte arrays may also be initialized with a Lua string. This
|
|
copies the whole string plus a terminating zero-byte. The copy stops
|
|
early only if the array has a known, fixed size.
|
|
|
|
=item * The fields of a C<struct> are initialized in the order of their
|
|
declaration. Uninitialized fields are filled with zero bytes.
|
|
|
|
=item * Only the first field of a C<union> can be initialized with a
|
|
flat initializer.
|
|
|
|
=item * Elements or fields which are aggregates themselves are
|
|
initialized with a I<single> initializer, but this may be a table
|
|
initializer or a compatible aggregate.
|
|
|
|
=item * Excess initializers cause an error.
|
|
|
|
=back
|
|
|
|
=head2 Table Initializers
|
|
|
|
The following rules apply if a Lua table is used to initialize an Array
|
|
or a C<struct>/C<union>:
|
|
|
|
=over
|
|
|
|
=item * If the table index C<[0]> is non-C<nil>, then the table is
|
|
assumed to be zero-based. Otherwise it's assumed to be one-based.
|
|
|
|
=item * Array elements, starting at index zero, are initialized
|
|
one-by-one with the consecutive table elements, starting at either
|
|
index C<[0]> or C<[1]>. This process stops at the first C<nil> table
|
|
element.
|
|
|
|
=item * If exactly one array element was initialized, it's repeated for
|
|
all the remaining elements. Otherwise all remaining uninitialized
|
|
elements are filled with zero bytes.
|
|
|
|
=item * The above logic only applies to arrays with a known fixed size.
|
|
A VLA is only initialized with the element(s) given in the table.
|
|
Depending on the use case, you may need to explicitly add a C<NULL> or
|
|
C<0> terminator to a VLA.
|
|
|
|
=item * A C<struct>/C<union> can be initialized in the order of the
|
|
declaration of its fields. Each field is initialized with consecutive
|
|
table elements, starting at either index C<[0]> or C<[1]>. This process
|
|
stops at the first C<nil> table element.
|
|
|
|
=item * Otherwise, if neither index C<[0]> nor C<[1]> is present, a
|
|
C<struct>/C<union> is initialized by looking up each field name (as a
|
|
string key) in the table. Each non-C<nil> value is used to initialize
|
|
the corresponding field.
|
|
|
|
=item * Uninitialized fields of a C<struct> are filled with zero bytes,
|
|
except for the trailing VLA of a VLS.
|
|
|
|
=item * Initialization of a C<union> stops after one field has been
|
|
initialized. If no field has been initialized, the C<union> is filled
|
|
with zero bytes.
|
|
|
|
=item * Elements or fields which are aggregates themselves are
|
|
initialized with a I<single> initializer, but this may be a nested
|
|
table initializer (or a compatible aggregate).
|
|
|
|
=item * Excess initializers for an array cause an error. Excess
|
|
initializers for a C<struct>/C<union> are ignored. Unrelated table
|
|
entries are ignored, too.
|
|
|
|
=back
|
|
|
|
Example:
|
|
|
|
local ffi = require("ffi")
|
|
|
|
ffi.cdef[[
|
|
struct foo { int a, b; };
|
|
union bar { int i; double d; };
|
|
struct nested { int x; struct foo y; };
|
|
]]
|
|
|
|
ffi.new("int[3]", {}) --> 0, 0, 0
|
|
ffi.new("int[3]", {1}) --> 1, 1, 1
|
|
ffi.new("int[3]", {1,2}) --> 1, 2, 0
|
|
ffi.new("int[3]", {1,2,3}) --> 1, 2, 3
|
|
ffi.new("int[3]", {[0]=1}) --> 1, 1, 1
|
|
ffi.new("int[3]", {[0]=1,2}) --> 1, 2, 0
|
|
ffi.new("int[3]", {[0]=1,2,3}) --> 1, 2, 3
|
|
ffi.new("int[3]", {[0]=1,2,3,4}) --> error: too many initializers
|
|
|
|
ffi.new("struct foo", {}) --> a = 0, b = 0
|
|
ffi.new("struct foo", {1}) --> a = 1, b = 0
|
|
ffi.new("struct foo", {1,2}) --> a = 1, b = 2
|
|
ffi.new("struct foo", {[0]=1,2}) --> a = 1, b = 2
|
|
ffi.new("struct foo", {b=2}) --> a = 0, b = 2
|
|
ffi.new("struct foo", {a=1,b=2,c=3}) --> a = 1, b = 2 'c' is ignored
|
|
|
|
ffi.new("union bar", {}) --> i = 0, d = 0.0
|
|
ffi.new("union bar", {1}) --> i = 1, d = ?
|
|
ffi.new("union bar", {[0]=1,2}) --> i = 1, d = ? '2' is ignored
|
|
ffi.new("union bar", {d=2}) --> i = ?, d = 2.0
|
|
|
|
ffi.new("struct nested", {1,{2,3}}) --> x = 1, y.a = 2, y.b = 3
|
|
ffi.new("struct nested", {x=1,y={2,3}}) --> x = 1, y.a = 2, y.b = 3
|
|
|
|
=head2 Operations on cdata Objects
|
|
|
|
All of the standard Lua operators can be applied to cdata objects or a
|
|
mix of a cdata object and another Lua object. The following list shows
|
|
the pre-defined operations.
|
|
|
|
Reference types are dereferenced I<before> performing each of the
|
|
operations below E<mdash> the operation is applied to the C type
|
|
pointed to by the reference.
|
|
|
|
The pre-defined operations are always tried first before deferring to a
|
|
metamethod or index table (if any) for the corresponding ctype (except
|
|
for C<__new>). An error is raised if the metamethod lookup or index
|
|
table lookup fails.
|
|
|
|
=head2 Indexing a cdata object
|
|
|
|
=over
|
|
|
|
=item * B<Indexing a pointer/array>: a cdata pointer/array can be
|
|
indexed by a cdata number or a Lua number. The element address is
|
|
computed as the base address plus the number value multiplied by the
|
|
element size in bytes. A read access loads the element value and
|
|
converts it to a Lua object. A write access converts a Lua object to
|
|
the element type and stores the converted value to the element. An
|
|
error is raised if the element size is undefined or a write access to a
|
|
constant element is attempted.
|
|
|
|
=item * B<Dereferencing a C<struct>/C<union> field>: a cdata
|
|
C<struct>/C<union> or a pointer to a C<struct>/C<union> can be
|
|
dereferenced by a string key, giving the field name. The field address
|
|
is computed as the base address plus the relative offset of the field.
|
|
A read access loads the field value and converts it to a Lua object. A
|
|
write access converts a Lua object to the field type and stores the
|
|
converted value to the field. An error is raised if a write access to a
|
|
constant C<struct>/C<union> or a constant field is attempted. Scoped
|
|
enum constants or static constants are treated like a constant field.
|
|
|
|
=item * B<Indexing a complex number>: a complex number can be indexed
|
|
either by a cdata number or a Lua number with the values 0 or 1, or by
|
|
the strings C<"re"> or C<"im">. A read access loads the real part
|
|
(C<[0]>, C<.re>) or the imaginary part (C<[1]>, C<.im>) part of a
|
|
complex number and converts it to a Lua number. The sub-parts of a
|
|
complex number are immutable E<mdash> assigning to an index of a
|
|
complex number raises an error. Accessing out-of-bound indexes returns
|
|
unspecified results, but is guaranteed not to trigger memory access
|
|
violations.
|
|
|
|
=item * B<Indexing a vector>: a vector is treated like an array for
|
|
indexing purposes, except the vector elements are immutable E<mdash>
|
|
assigning to an index of a vector raises an error.
|
|
|
|
=back
|
|
|
|
A ctype object can be indexed with a string key, too. The only
|
|
pre-defined operation is reading scoped constants of C<struct>/C<union>
|
|
types. All other accesses defer to the corresponding metamethods or
|
|
index tables (if any).
|
|
|
|
Note: since there's (deliberately) no address-of operator, a cdata
|
|
object holding a value type is effectively immutable after
|
|
initialization. The JIT compiler benefits from this fact when applying
|
|
certain optimizations.
|
|
|
|
As a consequence, the I<elements> of complex numbers and vectors are
|
|
immutable. But the elements of an aggregate holding these types I<may>
|
|
be modified of course. I.e. you cannot assign to C<foo.c.im>, but you
|
|
can assign a (newly created) complex number to C<foo.c>.
|
|
|
|
The JIT compiler implements strict aliasing rules: accesses to
|
|
different types do B<not> alias, except for differences in signedness
|
|
(this applies even to C<char> pointers, unlike C99). Type punning
|
|
through unions is explicitly detected and allowed.
|
|
|
|
=head2 Calling a cdata object
|
|
|
|
=over
|
|
|
|
=item * B<Constructor>: a ctype object can be called and used as a
|
|
constructor. This is equivalent to C<ffi.new(ct, ...)>, unless a
|
|
C<__new> metamethod is defined. The C<__new> metamethod is called with
|
|
the ctype object plus any other arguments passed to the contructor.
|
|
Note that you have to use C<ffi.new> inside of it, since calling
|
|
C<ct(...)> would cause infinite recursion.
|
|
|
|
=item * B<C function call>: a cdata function or cdata function pointer
|
|
can be called. The passed arguments are converted to the C types of the
|
|
parameters given by the function declaration. Arguments passed to the
|
|
variable argument part of vararg C function use special conversion
|
|
rules. This C function is called and the return value (if any) is
|
|
converted to a Lua object.
|
|
|
|
On Windows/x86 systems, C<__stdcall> functions are automatically
|
|
detected and a function declared as C<__cdecl> (the default) is
|
|
silently fixed up after the first call.
|
|
|
|
=back
|
|
|
|
=head2 Arithmetic on cdata objects
|
|
|
|
=over
|
|
|
|
=item * B<Pointer arithmetic>: a cdata pointer/array and a cdata number
|
|
or a Lua number can be added or subtracted. The number must be on the
|
|
right hand side for a subtraction. The result is a pointer of the same
|
|
type with an address plus or minus the number value multiplied by the
|
|
element size in bytes. An error is raised if the element size is
|
|
undefined.
|
|
|
|
=item * B<Pointer difference>: two compatible cdata pointers/arrays can
|
|
be subtracted. The result is the difference between their addresses,
|
|
divided by the element size in bytes. An error is raised if the element
|
|
size is undefined or zero.
|
|
|
|
=item * B<64 bit integer arithmetic>: the standard arithmetic operators
|
|
(C<+ - * / % ^> and unary minus) can be applied to two cdata numbers,
|
|
or a cdata number and a Lua number. If one of them is an C<uint64_t>,
|
|
the other side is converted to an C<uint64_t> and an unsigned
|
|
arithmetic operation is performed. Otherwise both sides are converted
|
|
to an C<int64_t> and a signed arithmetic operation is performed. The
|
|
result is a boxed 64 bit cdata object.
|
|
|
|
If one of the operands is an C<enum> and the other operand is a string,
|
|
the string is converted to the value of a matching C<enum> constant
|
|
before the above conversion.
|
|
|
|
These rules ensure that 64 bit integers are "sticky". Any expression
|
|
involving at least one 64 bit integer operand results in another one.
|
|
The undefined cases for the division, modulo and power operators return
|
|
C<2LL ^ 63> or C<2ULL ^ 63>.
|
|
|
|
You'll have to explicitly convert a 64 bit integer to a Lua number
|
|
(e.g. for regular floating-point calculations) with C<tonumber()>. But
|
|
note this may incur a precision loss.
|
|
|
|
=item * B<64 bit bitwise operations>: the rules for 64 bit arithmetic
|
|
operators apply analogously.
|
|
|
|
Unlike the other C<bit.*> operations, C<bit.tobit()> converts a cdata
|
|
number via C<int64_t> to C<int32_t> and returns a Lua number.
|
|
|
|
For C<bit.band()>, C<bit.bor()> and C<bit.bxor()>, the conversion to
|
|
C<int64_t> or C<uint64_t> applies to I<all> arguments, if I<any>
|
|
argument is a cdata number.
|
|
|
|
For all other operations, only the first argument is used to determine
|
|
the output type. This implies that a cdata number as a shift count for
|
|
shifts and rotates is accepted, but that alone does I<not> cause a
|
|
cdata number output.
|
|
|
|
=back
|
|
|
|
=head2 Comparisons of cdata objects
|
|
|
|
=over
|
|
|
|
=item * B<Pointer comparison>: two compatible cdata pointers/arrays can
|
|
be compared. The result is the same as an unsigned comparison of their
|
|
addresses. C<nil> is treated like a C<NULL> pointer, which is
|
|
compatible with any other pointer type.
|
|
|
|
=item * B<64 bit integer comparison>: two cdata numbers, or a cdata
|
|
number and a Lua number can be compared with each other. If one of them
|
|
is an C<uint64_t>, the other side is converted to an C<uint64_t> and an
|
|
unsigned comparison is performed. Otherwise both sides are converted to
|
|
an C<int64_t> and a signed comparison is performed.
|
|
|
|
If one of the operands is an C<enum> and the other operand is a string,
|
|
the string is converted to the value of a matching C<enum> constant
|
|
before the above conversion.
|
|
|
|
=item * B<Comparisons for equality/inequality> never raise an error.
|
|
Even incompatible pointers can be compared for equality by address. Any
|
|
other incompatible comparison (also with non-cdata objects) treats the
|
|
two sides as unequal.
|
|
|
|
=back
|
|
|
|
=head2 cdata objects as table keys
|
|
|
|
Lua tables may be indexed by cdata objects, but this doesn't provide
|
|
any useful semantics E<mdash> B<cdata objects are unsuitable as table
|
|
keys!>
|
|
|
|
A cdata object is treated like any other garbage-collected object and
|
|
is hashed and compared by its address for table indexing. Since there's
|
|
no interning for cdata value types, the same value may be boxed in
|
|
different cdata objects with different addresses. Thus C<t[1LL+1LL]>
|
|
and C<t[2LL]> usually B<do not> point to the same hash slot and they
|
|
certainly B<do not> point to the same hash slot as C<t[2]>.
|
|
|
|
It would seriously drive up implementation complexity and slow down the
|
|
common case, if one were to add extra handling for by-value hashing and
|
|
comparisons to Lua tables. Given the ubiquity of their use inside the
|
|
VM, this is not acceptable.
|
|
|
|
There are three viable alternatives, if you really need to use cdata
|
|
objects as keys:
|
|
|
|
=over
|
|
|
|
=item * If you can get by with the precision of Lua numbers (52 bits),
|
|
then use C<tonumber()> on a cdata number or combine multiple fields of
|
|
a cdata aggregate to a Lua number. Then use the resulting Lua number as
|
|
a key when indexing tables.
|
|
|
|
One obvious benefit: C<t[tonumber(2LL)]> B<does> point to the same slot
|
|
as C<t[2]>.
|
|
|
|
=item * Otherwise use either C<tostring()> on 64 bit integers or
|
|
complex numbers or combine multiple fields of a cdata aggregate to a
|
|
Lua string (e.g. with C<ffi.string()>). Then use the resulting Lua
|
|
string as a key when indexing tables.
|
|
|
|
=item * Create your own specialized hash table implementation using the
|
|
C types provided by the FFI library, just like you would in C code.
|
|
Ultimately this may give much better performance than the other
|
|
alternatives or what a generic by-value hash table could possibly
|
|
provide.
|
|
|
|
=back
|
|
|
|
=head2 Parameterized Types
|
|
|
|
To facilitate some abstractions, the two functions C<ffi.typeof> and
|
|
C<ffi.cdef> support parameterized types in C declarations. Note: none
|
|
of the other API functions taking a cdecl allow this.
|
|
|
|
Any place you can write a B<C<typedef> name>, an B<identifier> or a
|
|
B<number> in a declaration, you can write C<$> (the dollar sign)
|
|
instead. These placeholders are replaced in order of appearance with
|
|
the arguments following the cdecl string:
|
|
|
|
-- Declare a struct with a parameterized field type and name:
|
|
ffi.cdef([[
|
|
typedef struct { $ $; } foo_t;
|
|
]], type1, name1)
|
|
|
|
-- Anonymous struct with dynamic names:
|
|
local bar_t = ffi.typeof("struct { int $, $; }", name1, name2)
|
|
-- Derived pointer type:
|
|
local bar_ptr_t = ffi.typeof("$ *", bar_t)
|
|
|
|
-- Parameterized dimensions work even where a VLA won't work:
|
|
local matrix_t = ffi.typeof("uint8_t[$][$]", width, height)
|
|
|
|
Caveat: this is I<not> simple text substitution! A passed ctype or
|
|
cdata object is treated like the underlying type, a passed string is
|
|
considered an identifier and a number is considered a number. You must
|
|
not mix this up: e.g. passing C<"int"> as a string doesn't work in
|
|
place of a type, you'd need to use C<ffi.typeof("int")> instead.
|
|
|
|
The main use for parameterized types are libraries implementing
|
|
abstract data types (example), similar to what can be achieved with C++
|
|
template metaprogramming. Another use case are derived types of
|
|
anonymous structs, which avoids pollution of the global struct
|
|
namespace.
|
|
|
|
Please note that parameterized types are a nice tool and indispensable
|
|
for certain use cases. But you'll want to use them sparingly in regular
|
|
code, e.g. when all types are actually fixed.
|
|
|
|
=head2 Garbage Collection of cdata Objects
|
|
|
|
All explicitly (C<ffi.new()>, C<ffi.cast()> etc.) or implicitly
|
|
(accessors) created cdata objects are garbage collected. You need to
|
|
ensure to retain valid references to cdata objects somewhere on a Lua
|
|
stack, an upvalue or in a Lua table while they are still in use. Once
|
|
the last reference to a cdata object is gone, the garbage collector
|
|
will automatically free the memory used by it (at the end of the next
|
|
GC cycle).
|
|
|
|
Please note that pointers themselves are cdata objects, however they
|
|
are B<not> followed by the garbage collector. So e.g. if you assign a
|
|
cdata array to a pointer, you must keep the cdata object holding the
|
|
array alive as long as the pointer is still in use:
|
|
|
|
ffi.cdef[[
|
|
typedef struct { int *a; } foo_t;
|
|
]]
|
|
|
|
local s = ffi.new("foo_t", ffi.new("int[10]")) -- WRONG!
|
|
|
|
local a = ffi.new("int[10]") -- OK
|
|
local s = ffi.new("foo_t", a)
|
|
-- Now do something with 's', but keep 'a' alive until you're done.
|
|
|
|
Similar rules apply for Lua strings which are implicitly converted to
|
|
C<"const char *">: the string object itself must be referenced
|
|
somewhere or it'll be garbage collected eventually. The pointer will
|
|
then point to stale data, which may have already been overwritten. Note
|
|
that I<string literals> are automatically kept alive as long as the
|
|
function containing it (actually its prototype) is not garbage
|
|
collected.
|
|
|
|
Objects which are passed as an argument to an external C function are
|
|
kept alive until the call returns. So it's generally safe to create
|
|
temporary cdata objects in argument lists. This is a common idiom for
|
|
passing specific C types to vararg functions.
|
|
|
|
Memory areas returned by C functions (e.g. from C<malloc()>) must be
|
|
manually managed, of course (or use C<ffi.gc()>). Pointers to cdata
|
|
objects are indistinguishable from pointers returned by C functions
|
|
(which is one of the reasons why the GC cannot follow them).
|
|
|
|
=head2 Callbacks
|
|
|
|
The LuaJIT FFI automatically generates special callback functions
|
|
whenever a Lua function is converted to a C function pointer. This
|
|
associates the generated callback function pointer with the C type of
|
|
the function pointer and the Lua function object (closure).
|
|
|
|
This can happen implicitly due to the usual conversions, e.g. when
|
|
passing a Lua function to a function pointer argument. Or you can use
|
|
C<ffi.cast()> to explicitly cast a Lua function to a C function
|
|
pointer.
|
|
|
|
Currently only certain C function types can be used as callback
|
|
functions. Neither C vararg functions nor functions with pass-by-value
|
|
aggregate argument or result types are supported. There are no
|
|
restrictions for the kind of Lua functions that can be called from the
|
|
callback E<mdash> no checks for the proper number of arguments are
|
|
made. The return value of the Lua function will be converted to the
|
|
result type and an error will be thrown for invalid conversions.
|
|
|
|
It's allowed to throw errors across a callback invocation, but it's not
|
|
advisable in general. Do this only if you know the C function, that
|
|
called the callback, copes with the forced stack unwinding and doesn't
|
|
leak resources.
|
|
|
|
One thing that's not allowed, is to let an FFI call into a C function
|
|
get JIT-compiled, which in turn calls a callback, calling into Lua
|
|
again. Usually this attempt is caught by the interpreter first and the
|
|
C function is blacklisted for compilation.
|
|
|
|
However, this heuristic may fail under specific circumstances: e.g. a
|
|
message polling function might not run Lua callbacks right away and the
|
|
call gets JIT-compiled. If it later happens to call back into Lua (e.g.
|
|
a rarely invoked error callback), you'll get a VM PANIC with the
|
|
message C<"bad callback">. Then you'll need to manually turn off
|
|
JIT-compilation with C<jit.off()> for the surrounding Lua function that
|
|
invokes such a message polling function (or similar).
|
|
|
|
=head2 Callback resource handling
|
|
|
|
Callbacks take up resources E<mdash> you can only have a limited number
|
|
of them at the same time (500 - 1000, depending on the architecture).
|
|
The associated Lua functions are anchored to prevent garbage
|
|
collection, too.
|
|
|
|
B<Callbacks due to implicit conversions are permanent!> There is no way
|
|
to guess their lifetime, since the C side might store the function
|
|
pointer for later use (typical for GUI toolkits). The associated
|
|
resources cannot be reclaimed until termination:
|
|
|
|
ffi.cdef[[
|
|
typedef int (__stdcall *WNDENUMPROC)(void *hwnd, intptr_t l);
|
|
int EnumWindows(WNDENUMPROC func, intptr_t l);
|
|
]]
|
|
|
|
-- Implicit conversion to a callback via function pointer argument.
|
|
local count = 0
|
|
ffi.C.EnumWindows(function(hwnd, l)
|
|
count = count + 1
|
|
return true
|
|
end, 0)
|
|
-- The callback is permanent and its resources cannot be reclaimed!
|
|
-- Ok, so this may not be a problem, if you do this only once.
|
|
|
|
Note: this example shows that you I<must> properly declare C<__stdcall>
|
|
callbacks on Windows/x86 systems. The calling convention cannot be
|
|
automatically detected, unlike for C<__stdcall> calls I<to> Windows
|
|
functions.
|
|
|
|
For some use cases it's necessary to free up the resources or to
|
|
dynamically redirect callbacks. Use an explicit cast to a C function
|
|
pointer and keep the resulting cdata object. Then use the C<cb:free()>
|
|
or C<cb:set()> methods on the cdata object:
|
|
|
|
-- Explicitly convert to a callback via cast.
|
|
local count = 0
|
|
local cb = ffi.cast("WNDENUMPROC", function(hwnd, l)
|
|
count = count + 1
|
|
return true
|
|
end)
|
|
|
|
-- Pass it to a C function.
|
|
ffi.C.EnumWindows(cb, 0)
|
|
-- EnumWindows doesn't need the callback after it returns, so free it.
|
|
|
|
cb:free()
|
|
-- The callback function pointer is no longer valid and its resources
|
|
-- will be reclaimed. The created Lua closure will be garbage collected.
|
|
|
|
=head2 Callback performance
|
|
|
|
B<Callbacks are slow!> First, the C to Lua transition itself has an
|
|
unavoidable cost, similar to a C<lua_call()> or C<lua_pcall()>.
|
|
Argument and result marshalling add to that cost. And finally, neither
|
|
the C compiler nor LuaJIT can inline or optimize across the language
|
|
barrier and hoist repeated computations out of a callback function.
|
|
|
|
Do not use callbacks for performance-sensitive work: e.g. consider a
|
|
numerical integration routine which takes a user-defined function to
|
|
integrate over. It's a bad idea to call a user-defined Lua function
|
|
from C code millions of times. The callback overhead will be absolutely
|
|
detrimental for performance.
|
|
|
|
It's considerably faster to write the numerical integration routine
|
|
itself in Lua E<mdash> the JIT compiler will be able to inline the
|
|
user-defined function and optimize it together with its calling
|
|
context, with very competitive performance.
|
|
|
|
As a general guideline: B<use callbacks only when you must>, because of
|
|
existing C APIs. E.g. callback performance is irrelevant for a GUI
|
|
application, which waits for user input most of the time, anyway.
|
|
|
|
For new designs B<avoid push-style APIs>: a C function repeatedly
|
|
calling a callback for each result. Instead B<use pull-style APIs>:
|
|
call a C function repeatedly to get a new result. Calls from Lua to C
|
|
via the FFI are much faster than the other way round. Most
|
|
well-designed libraries already use pull-style APIs (read/write,
|
|
get/put).
|
|
|
|
=head2 C Library Namespaces
|
|
|
|
A C library namespace is a special kind of object which allows access
|
|
to the symbols contained in shared libraries or the default symbol
|
|
namespace. The default C<ffi.C> namespace is automatically created when
|
|
the FFI library is loaded. C library namespaces for specific shared
|
|
libraries may be created with the C<ffi.load()> API function.
|
|
|
|
Indexing a C library namespace object with a symbol name (a Lua string)
|
|
automatically binds it to the library. First the symbol type is
|
|
resolved E<mdash> it must have been declared with C<ffi.cdef>. Then the
|
|
symbol address is resolved by searching for the symbol name in the
|
|
associated shared libraries or the default symbol namespace. Finally,
|
|
the resulting binding between the symbol name, the symbol type and its
|
|
address is cached. Missing symbol declarations or nonexistent symbol
|
|
names cause an error.
|
|
|
|
This is what happens on a B<read access> for the different kinds of
|
|
symbols:
|
|
|
|
=over
|
|
|
|
=item * External functions: a cdata object with the type of the
|
|
function and its address is returned.
|
|
|
|
=item * External variables: the symbol address is dereferenced and the
|
|
loaded value is converted to a Lua object and returned.
|
|
|
|
=item * Constant values (C<static const> or C<enum> constants): the
|
|
constant is converted to a Lua object and returned.
|
|
|
|
=back
|
|
|
|
This is what happens on a B<write access>:
|
|
|
|
=over
|
|
|
|
=item * External variables: the value to be written is converted to the
|
|
C type of the variable and then stored at the symbol address.
|
|
|
|
=item * Writing to constant variables or to any other symbol type
|
|
causes an error, like any other attempted write to a constant location.
|
|
|
|
=back
|
|
|
|
C library namespaces themselves are garbage collected objects. If the
|
|
last reference to the namespace object is gone, the garbage collector
|
|
will eventually release the shared library reference and remove all
|
|
memory associated with the namespace. Since this may trigger the
|
|
removal of the shared library from the memory of the running process,
|
|
it's generally I<not safe> to use function cdata objects obtained from
|
|
a library if the namespace object may be unreferenced.
|
|
|
|
Performance notice: the JIT compiler specializes to the identity of
|
|
namespace objects and to the strings used to index it. This effectively
|
|
turns function cdata objects into constants. It's not useful and
|
|
actually counter-productive to explicitly cache these function objects,
|
|
e.g. C<local strlen = ffi.C.strlen>. OTOH it I<is> useful to cache the
|
|
namespace itself, e.g. C<local C = ffi.C>.
|
|
|
|
=head2 No Hand-holding!
|
|
|
|
The FFI library has been designed as B<a low-level library>. The goal
|
|
is to interface with C code and C data types with a minimum of
|
|
overhead. This means B<you can do anything you can do from C>: access
|
|
all memory, overwrite anything in memory, call machine code at any
|
|
memory address and so on.
|
|
|
|
The FFI library provides B<no memory safety>, unlike regular Lua code.
|
|
It will happily allow you to dereference a C<NULL> pointer, to access
|
|
arrays out of bounds or to misdeclare C functions. If you make a
|
|
mistake, your application might crash, just like equivalent C code
|
|
would.
|
|
|
|
This behavior is inevitable, since the goal is to provide full
|
|
interoperability with C code. Adding extra safety measures, like bounds
|
|
checks, would be futile. There's no way to detect misdeclarations of C
|
|
functions, since shared libraries only provide symbol names, but no
|
|
type information. Likewise there's no way to infer the valid range of
|
|
indexes for a returned pointer.
|
|
|
|
Again: the FFI library is a low-level library. This implies it needs to
|
|
be used with care, but it's flexibility and performance often outweigh
|
|
this concern. If you're a C or C++ developer, it'll be easy to apply
|
|
your existing knowledge. OTOH writing code for the FFI library is not
|
|
for the faint of heart and probably shouldn't be the first exercise for
|
|
someone with little experience in Lua, C or C++.
|
|
|
|
As a corollary of the above, the FFI library is B<not safe for use by
|
|
untrusted Lua code>. If you're sandboxing untrusted Lua code, you
|
|
definitely don't want to give this code access to the FFI library or to
|
|
I<any> cdata object (except 64 bit integers or complex numbers). Any
|
|
properly engineered Lua sandbox needs to provide safety wrappers for
|
|
many of the standard Lua library functions E<mdash> similar wrappers
|
|
need to be written for high-level operations on FFI data types, too.
|
|
|
|
=head2 Current Status
|
|
|
|
The initial release of the FFI library has some limitations and is
|
|
missing some features. Most of these will be fixed in future releases.
|
|
|
|
C language support is currently incomplete:
|
|
|
|
=over
|
|
|
|
=item * C declarations are not passed through a C pre-processor, yet.
|
|
|
|
=item * The C parser is able to evaluate most constant expressions
|
|
commonly found in C header files. However it doesn't handle the full
|
|
range of C expression semantics and may fail for some obscure
|
|
constructs.
|
|
|
|
=item * C<static const> declarations only work for integer types up to
|
|
32 bits. Neither declaring string constants nor floating-point
|
|
constants is supported.
|
|
|
|
=item * Packed C<struct> bitfields that cross container boundaries are
|
|
not implemented.
|
|
|
|
=item * Native vector types may be defined with the GCC C<mode> or
|
|
C<vector_size> attribute. But no operations other than loading, storing
|
|
and initializing them are supported, yet.
|
|
|
|
=item * The C<volatile> type qualifier is currently ignored by compiled
|
|
code.
|
|
|
|
=item * C<ffi.cdef> silently ignores most re-declarations. Note: avoid
|
|
re-declarations which do not conform to C99. The implementation will
|
|
eventually be changed to perform strict checks.
|
|
|
|
=back
|
|
|
|
The JIT compiler already handles a large subset of all FFI operations.
|
|
It automatically falls back to the interpreter for unimplemented
|
|
operations (you can check for this with the C<-jv> command line
|
|
option). The following operations are currently not compiled and may
|
|
exhibit suboptimal performance, especially when used in inner loops:
|
|
|
|
=over
|
|
|
|
=item * Vector operations.
|
|
|
|
=item * Table initializers.
|
|
|
|
=item * Initialization of nested C<struct>/C<union> types.
|
|
|
|
=item * Non-default initialization of VLA/VLS or large C types (E<gt>
|
|
128 bytes or E<gt> 16 array elements.
|
|
|
|
=item * Bitfield initializations.
|
|
|
|
=item * Pointer differences for element sizes that are not a power of
|
|
two.
|
|
|
|
=item * Calls to C functions with aggregates passed or returned by
|
|
value.
|
|
|
|
=item * Calls to ctype metamethods which are not plain functions.
|
|
|
|
=item * ctype C<__newindex> tables and non-string lookups in ctype
|
|
C<__index> tables.
|
|
|
|
=item * C<tostring()> for cdata types.
|
|
|
|
=item * Calls to C<ffi.cdef()>, C<ffi.load()> and C<ffi.metatype()>.
|
|
|
|
=back
|
|
|
|
Other missing features:
|
|
|
|
=over
|
|
|
|
=item * Arithmetic for C<complex> numbers.
|
|
|
|
=item * Passing structs by value to vararg C functions.
|
|
|
|
=item * C++ exception interoperability does not extend to C functions
|
|
called via the FFI, if the call is compiled.
|
|
|
|
=back
|
|
|
|
----
|
|
|
|
Copyright E<copy> 2005-2017 Mike Pall E<middot> Contact
|
|
|
|
=cut
|
|
|
|
#Pod::HTML2Pod conversion notes:
|
|
#From file ext_ffi_semantics.html
|
|
# 53732 bytes of input
|
|
#Mon May 14 13:19:16 2018 agentzh
|
|
# No a_name switch not specified, so will not try to render <a name='...'>
|
|
# No a_href switch not specified, so will not try to render <a href='...'>
|