FFI enums are now always boxed

  • From: Mike Pall <mike-1207@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Tue, 17 Jul 2012 23:08:00 +0200

I've just pushed a commit to LuaJIT git HEAD which always boxes
enums. Accesses to enums inside a struct/union/array or enums
returned from a called C function are boxed inside a newly created
enum cdata object. Note that allocations for temporary boxes can
usually be eliminated by the JIT compiler.

Previously the returned enum was immediately converted to a number
and its ctype was lost.

Example:

  ffi.cdef[[
  enum Foo { AAA = 1, BBB = 2 };
  struct Bar { int i; enum Foo e; double d; }
  enum Foo myfunc(int);
  void myfunc_enumarg(enum Foo);
  ]]

  bar = ffi.new("struct Bar")
  ...
  print(bar.i)               -- Returns plain Lua number
  print(bar.d)               -- Returns plain Lua number
  print(bar.e)               -- Returns boxed enum cdata object (NEW)
  print(mylib.myfunc(42))    -- Returns boxed enum cdata object (NEW)

The returned enum cdata object behaves like an integer wrt. the FFI
and converts readily back and forth to numbers. So you can e.g. pass
such an enum object to an integer argument or store it to a double
field and so on:

  bar.i = bar.e              -- Implicit conversion from enum
  bar.d = bar.e              -- Implicit conversion from enum
  bar.e = bar.i              -- Implicit conversion to enum
  mylib.myfunc(bar.e)        -- Implicit conversion from enum
  mylib.myfunc_enumarg(42)   -- Implicit conversion to enum

This change may break compatibility with existing programs using
the FFI, if you pass an enum to a non-FFI function. Hopefully this
is not very common. You may need to explicitly convert the enum to
a plain Lua number with tonumber(). Or better declare the field or
return value as an integer, if it's usually treated like a number.

A new feature is that comparisons and arithmetic operations
automatically convert a string operand that matches an enum
constant to its value, provided the other operand is an enum. This
is the same implicit conversion that's used for storing to enum
fields/elements or passing enum arguments (already worked before):

  bar.e = "AAA"              -- Same as bar.e = 1
  mylib.myfunc("AAA")        -- Same as mylib.myfunc(1)

  if bar.e == "BBB" then ... end
                             -- Same as bar.e == 2 (NEW)
  if mylib.myfunc(42) == "BBB" then ... end
                             -- Same as mylib.myfunc(42) == 2 (NEW)

Here's an example from the expat API:

  ffi.cdef[[
  enum XML_Status {
    XML_STATUS_ERROR = 0,
    XML_STATUS_OK = 1,
    XML_STATUS_SUSPENDED = 2
  };
  // ... Needs more declarations ...
  enum XML_Status XML_StopParser(XML_Parser parser, uint8_t resumable);
  ]]

  if lib.XML_StopParser(parser, 1) ~= "XML_STATUS_OK" then
    ...
  end

You couldn't do this before, because the enum type was lost with
the implicit conversion to a number (and a number never compares
equal to a string).

--Mike

Other related posts: