Re: Overriding FFI ctype constructors?

  • From: Mike Pall <mike-1206@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Fri, 8 Jun 2012 19:53:25 +0200

Claire Lewis wrote:
> Mike Pall wrote:
> > In the general case, you'll want access to plain object creation
> > (if the size is known) or to one of several user-defined
> > constructors. Now, how do you differentiate those cases? If you
> > can override the default constructor with your own function, then
> > how do you create the object inside of that function, if needed?
> 
> I suppose I had thought overriding it would preclude "simple"
> creation of the struct, but that's not a very orthogonal
> solution either.
> 
> Maybe it would always be possibel to use the string form,
> ffi.new("struct blah") or somesuch.

The C++ solution is to always pass an allocated (uninitialized)
piece of memory to the constructor.

But that's not a perfect solution for the FFI case. That would
allow a custom initializer, but not an allocator+initializer (e.g.
your wrapped C++ constructor).

> I could definitely see users wanting to have access to
> ffi.istype().  Say, we might have (opaque) structs representing
> a Vector and a Matrix; someone wants to write a Lua function (or
> __mul metatable entry) that can take either; they'll need some
> mechanism to do so.
> 
> I could provide my own IsType(), though I can't use the
> (potentially cdata) constructor as a table key, so that's going
> to be tedious.

Or provide IsVector() and/or IsMatrix() functions. But yes, you'd
have to do that for every type, which becomes a bit tedious.

> I understand FFI is a low level API, and perhaps I just need to
> wrap the whole thing.  I just don't want to get any unnecessary
> inefficiencies, or break effective JIT optimisation by adding
> too many layers to the code.

That's certainly a good idea. You can always write some simple
test cases that call a function in a 'for i=1,100 do' loop and
carefully watch the output of -jdump.

> So this brings me to my current thought for a solution; make
> everything return a "constructor" that is in fact a table with a
> __call method to construct, but also contains some ".type" key,
> for things that need to get the ctype.

Not too bad, but still an extra indirection.

Another option is to directly return the constructor (which is
used way more often) and then map that to the type with a table.
This still works even if the constructor is a ctype. Because
you're giving users only one instance of it and that works just
fine as a unique table key.

Something like this:

  realtype = {}

  OpaqueType_ = ffi.typeof("struct OpaqueType")
  OpaqueType = mylib.OpaqueType_new()
  realtype[OpaqueType] = OpaqueType_

  NonOpaqueType = ffi.typeof("struct NonOpaqueType")
  realtype[NonOpaqueType] = NonOpaqueType

--Mike

Other related posts: