FFI: Add ctype metamethods and ffi.metatype().
This commit is contained in:
@@ -238,6 +238,31 @@ This functions is mainly useful to override the pointer compatibility
|
||||
checks or to convert pointers to addresses or vice versa.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_metatype"><tt>ctype = ffi.metatype(ct, metatable)</tt></h3>
|
||||
<p>
|
||||
Creates a ctype object for the given <tt>ct</tt> and associates it with
|
||||
a metatable. Only <tt>struct</tt>/<tt>union</tt> types, complex numbers
|
||||
and vectors are allowed. Other types may be wrapped in a
|
||||
<tt>struct</tt>, if needed.
|
||||
</p>
|
||||
<p>
|
||||
The association with a metatable is permanent and cannot be changed
|
||||
afterwards. Neither the contents of the <tt>metatable</tt> nor the
|
||||
contents of an <tt>__index</tt> table (if any) may be modified
|
||||
afterwards. The associated metatable automatically applies to all uses
|
||||
of this type, no matter how the objects are created or where they
|
||||
originate from. Note that pre-defined operations on types have
|
||||
precedence (e.g. declared field names cannot be overriden).
|
||||
</p>
|
||||
<p>
|
||||
All standard Lua metamethods are implemented. These are called directly,
|
||||
without shortcuts and on any mix of types. For binary operations, the
|
||||
left operand is checked first for a valid ctype metamethod. The
|
||||
<tt>__gc</tt> metamethod only applies to <tt>struct</tt>/<tt>union</tt>
|
||||
types and performs an implicit <a href="#ffi_gc"><tt>ffi.gc()</tt></a>
|
||||
call during creation of an instance.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_gc"><tt>cdata = ffi.gc(cdata, finalizer)</tt></h3>
|
||||
<p>
|
||||
Associates a finalizer with a pointer or aggregate cdata object. The
|
||||
|
||||
@@ -582,6 +582,10 @@ Reference types are dereferenced <em>before</em> performing each of
|
||||
the operations below — the operation is applied to the
|
||||
C type pointed to by the reference.
|
||||
</p>
|
||||
<p>
|
||||
The pre-defined operations are always tried first before deferring to a
|
||||
metamethod for a ctype (if defined).
|
||||
</p>
|
||||
|
||||
<h3 id="cdata_array">Indexing a cdata object</h3>
|
||||
<ul>
|
||||
@@ -803,9 +807,10 @@ vararg functions</a>.
|
||||
</p>
|
||||
<p>
|
||||
Memory areas returned by C functions (e.g. from <tt>malloc()</tt>)
|
||||
must be manually managed, of course. Pointers to cdata objects are
|
||||
indistinguishable from pointers returned by C functions (which is one
|
||||
of the reasons why the GC cannot follow them).
|
||||
must be manually managed, of course (or use
|
||||
<a href="ext_ffi_api.html#ffi_gc"><tt>ffi.gc()</tt></a>)). Pointers to
|
||||
cdata objects are indistinguishable from pointers returned by C
|
||||
functions (which is one of the reasons why the GC cannot follow them).
|
||||
</p>
|
||||
|
||||
<h2 id="clib">C Library Namespaces</h2>
|
||||
@@ -977,6 +982,9 @@ two.</li>
|
||||
value.</li>
|
||||
<li>Calls to C functions with 64 bit arguments or return values
|
||||
on 32 bit CPUs.</li>
|
||||
<li>Calls to ctype metamethods which are not plain functions.</li>
|
||||
<li>ctype <tt>__newindex</tt> tables and non-string lookups in ctype
|
||||
<tt>__index</tt> tables.</li>
|
||||
<li>Accesses to external variables in C library namespaces.</li>
|
||||
<li><tt>tostring()</tt> for cdata types.</li>
|
||||
<li>The following <a href="ext_ffi_api.html">ffi.* API</a> functions:
|
||||
@@ -988,7 +996,6 @@ Other missing features:
|
||||
<ul>
|
||||
<li>Bit operations for 64 bit types.</li>
|
||||
<li>Arithmetic for <tt>complex</tt> numbers.</li>
|
||||
<li>User-defined metamethods for C types.</li>
|
||||
<li>Callbacks from C code to Lua functions.</li>
|
||||
<li>Atomic handling of <tt>errno</tt>.</li>
|
||||
<li>Passing structs by value to vararg C functions.</li>
|
||||
|
||||
@@ -386,6 +386,99 @@ application might work on some systems, but would fail in a POSIX/x64
|
||||
environment.
|
||||
</p>
|
||||
|
||||
<h2 id="metatype">Defining Metamethods for a C Type</h2>
|
||||
<p>
|
||||
The following code explains how to define metamethods for a C type.
|
||||
We define a simple point type and add some operations to it:
|
||||
</p>
|
||||
<pre class="code mark">
|
||||
<span class="codemark">
|
||||
①
|
||||
|
||||
|
||||
|
||||
②
|
||||
|
||||
③
|
||||
|
||||
④
|
||||
|
||||
|
||||
|
||||
⑤
|
||||
|
||||
⑥</span>local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
<span style="color:#00a000;">typedef struct { double x, y; } point_t;</span>
|
||||
]]
|
||||
|
||||
local point
|
||||
local mt = {
|
||||
__add = function(a, b) return point(a.x+b.x, a.y+b.y) end,
|
||||
__len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,
|
||||
__index = {
|
||||
area = function(a) return a.x*a.x + a.y*a.y end,
|
||||
},
|
||||
}
|
||||
point = ffi.metatype("point_t", mt)
|
||||
|
||||
local a = point(3, 4)
|
||||
print(a.x, a.y) --> 3 4
|
||||
print(#a) --> 5
|
||||
print(a:area()) --> 25
|
||||
local b = a + point(0.5, 8)
|
||||
print(#b) --> 12.5
|
||||
</pre>
|
||||
<p>
|
||||
Here's the step-by-step explanation:
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">①</span> This defines the C type for a
|
||||
two-dimensional point object.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">②</span> We have to declare the variable
|
||||
holding the point constructor first, because it's used inside of a
|
||||
metamethod.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">③</span> Let's define an <tt>__add</tt>
|
||||
metamethod which adds the coordinates of two points and creates a new
|
||||
point object. For simplicity, this function assumes that both arguments
|
||||
are points. But it could be any mix of objects, if at least one operand
|
||||
is of the required type (e.g. adding a point plus a number or vice
|
||||
versa). Our <tt>__len</tt> metamethod returns the distance of a point to
|
||||
the origin.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">④</span> If we run out of operators, we can
|
||||
define named methods, too. Here the <tt>__index</tt> table defines an
|
||||
<tt>area</tt> function. For custom indexing needs, one might want to
|
||||
define <tt>__index</tt> and <tt>__newindex</tt> functions instead.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑤</span> This associates the metamethods with
|
||||
our C type. This only needs to be done once. For convenience, a
|
||||
constructor is returned by
|
||||
<a href="ffi_ext_api.html#ffi_metatype"><tt>ffi.metatype()</tt></a>.
|
||||
We're not required to use it, though. The original C type can still
|
||||
be used e.g. to create an array of points. The metamethods automatically
|
||||
apply to any and all uses of this type.
|
||||
</p>
|
||||
<p>
|
||||
Please note that the association with a metatable is permanent and
|
||||
<b>the metatable must not be modified afterwards!</b> Ditto for the
|
||||
<tt>__index</tt> table.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑥</span> Here are some simple usage examples
|
||||
for the point type and their expected results. The pre-defined
|
||||
operations (such as <tt>a.x</tt>) can be freely mixed with the newly
|
||||
defined metamethods. Note that <tt>area</tt> is a method and must be
|
||||
called with the Lua syntax for methods: <tt>a:area()</tt>, not
|
||||
<tt>a.area()</tt>.
|
||||
</p>
|
||||
|
||||
<h2 id="idioms">Translating C Idioms</h2>
|
||||
<p>
|
||||
Here's a list of common C idioms and their translation to the
|
||||
|
||||
Reference in New Issue
Block a user