On Sun, May 29, 2011 at 3:18 PM, Duncan Cross
<duncan.cross@gmail.com> wrote:
Say I'd like to have a function which takes a value that represents a
buffer store of bytes, and returns two values:
- a value that can be ffi.cast to a void* (or other pointer)
- the number of bytes that this pointer-value points to
(1) If the input value is a string, the return values would be a
char[?] cdata with the contents of the string copied into it, and the
length of the original string. (The buffer would have an extra byte
added for the null terminator, but this would not be included in the
returned length.)
(2) If the input value is one of a set of special cdata types, the two
return values would be taken from ._ptr and ._byteLength fields on the
cdata.
(3) If the input value is not one of one these special types, but it
*is* a cdata that is a struct or an array, the two return values would
be the original input value and ffi.sizeof the input value.
(4) Finally, if the input value (most likely a table) has a special
custom __tobuffer() metamethod on its metatable, this is called on it
to get the two values.
Hopefully this makes sense so far.
These are the issues I'm having with implementing this:
A. Determining 'one of a set of special cdata types' (2). I have tried
using tostring(ffi.typeof(value)) and looking this up in a table, and
it seems to work, but should I trust this method to be future-proof?
No thats not the recommended method, you should use ffi.istype(type, val) which returns true if it is that type.
B. Determining whether a cdata is a struct/array (3) rather than, for
example, a void* pointer where ffi.sizeof would be the size of the
pointer rather than the size of what it's pointing to. Again, I have
thought of making use of tostring(ffi.typeof(value)) - seeing if it
begins with 'ctype<struct' or ends in ']>' - but this seems like an
even more fragile hack. It would be ideal if there were something like
a special field on ctype values to determine this.
This sounds very flaky. When calling functions that need a buffer I have generally gone with providing a buffer type (with constructor), and accepting strings, or accepting any type if a length is provided in situations like this. You could in addition support duck typing, ie also accept anything that has a ptr and len method as well. A generic function to extract ptr, len just isnt going to always work I dont think...
Also, it strikes me that stage (2) would not be necessary if it were
possible to apply (4) to these cdata values instead of special-casing
them. I know there are heavy restrictions on the metatables of cdata,
and that is understandable. But I'm wondering if it would be possible
to call custom metamethods on cdata with something like a
ffi.metacall(cdata, '__mymetamethod')?
Why cant you just add a __tobuffer metamethod to all the types?
Thanks,
-Duncan