FFI: Add ctype metamethods and ffi.metatype().

This commit is contained in:
Mike Pall
2011-04-12 19:15:00 +02:00
parent fa5cd010e8
commit 3b6f37dd2c
11 changed files with 426 additions and 83 deletions

View File

@@ -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

View File

@@ -582,6 +582,10 @@ Reference types are dereferenced <em>before</em> performing each of
the operations below &mdash; the operation is applied to the
C&nbsp;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&nbsp;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&nbsp;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&nbsp;bit types.</li>
<li>Arithmetic for <tt>complex</tt> numbers.</li>
<li>User-defined metamethods for C&nbsp;types.</li>
<li>Callbacks from C&nbsp;code to Lua functions.</li>
<li>Atomic handling of <tt>errno</tt>.</li>
<li>Passing structs by value to vararg C&nbsp;functions.</li>

View File

@@ -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&nbsp;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">&nbsp;
&#9312;
&#9313;
&#9314;
&#9315;
&#9316;
&#9317;</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">&#9312;</span> This defines the C&nbsp;type for a
two-dimensional point object.
</p>
<p>
<span class="mark">&#9313;</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">&#9314;</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">&#9315;</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">&#9316;</span> This associates the metamethods with
our C&nbsp;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&nbsp;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">&#9317;</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&nbsp;Idioms</h2>
<p>
Here's a list of common C&nbsp;idioms and their translation to the