lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


2011/12/15 Ezequiel García <elezegarcia@yahoo.com.ar>:
> Hi!
> I am working on a Lua binding for a library that makes extensive use
> of enums to pass on flags, capabilities, and such. Like this:
>
> typedef enum {
>     UNKNOWN    = 0x00000000,
>     FRONTONLY  = 0x00000001,
>     BACKVIDEO  = 0x00000002,
>     BACKSYSTEM = 0x00000004,
>     TRIPLE     = 0x00000008,
>     WINDOWS    = 0x00000010
> } BufferMode;
>
> So I would like to know the community opinion on this: How to implement this kind of C enums?
>
> So far, my solution is this: translate each symbol of the like 'UNKNOWN'
> into a Lua numeric global variable. The global is not mandatory, for I could
> pushed it into the module itself. But, in that case, I would have to type:
>
>  module_name.UNKNOWN
>
> each time and I am too lazy for that.
>
> The numeric part comes because I want to add flags and such, like this:
>
>  buffermode = TRIPLE + WINDOWS
>
> Fortunately I don't need to OR, and if needed one could use any bit library.
> The cons of this solution is not having any "type checking" or somekind
> of "type enforcement" since variables or just variables, I could even mispell
> and end up with a nil value.
> The pro of the solution is (at least to me): ease of implementation, ease of usage.
>
> So, what do you think?

In my win32 bindings I needed something like quite often. In the end,
I wrote helper functions called lua_toDWORDenum, lua_toDWORDflags,
with variants with lua_is*, luaL_check* and luaL_opt* prefixes, and
various C types instead of DWORD.

The "enum" variants expect a single value, but that can be either nil,
a number, a light userdata, or a string. The first three are taken as
the value itself (nil is 0, I check numbers are integers, and I
convert the light userdata void* to an integer). A string is
interpreted as the name of the enum inside some table (my root win32
module).

The "flags" variants accept the same values as the "enum" variants,
and in addition to that a table, which can be a set of enums, an array
of enums, or the combination of the two.

All these helper functions return an integer of the appropriate C type
(DWORD for lua_toDWORDflags, etc.).

For example, all the following calls to MessageBox are equivalent :

local win32 = require 'win32'
local user32 = require 'win32.user32'

user32.MessageBox(nil, "Hello World!", "Lua", win32.MB_OK + win32.MB_ICONERROR)
user32.MessageBox(nil, "Hello World!", "Lua", {win32.MB_OK, win32.MB_ICONERROR})
user32.MessageBox(nil, "Hello World!", "Lua", {'MB_OK', 'MB_ICONERROR'})
user32.MessageBox(nil, "Hello World!", "Lua", {MB_OK = true,
MB_ICONERROR = true})
user32.MessageBox(nil, "Hello World!", "Lua", {[win32.MB_OK] = true,
"MB_ICONERROR")
-- and since MB_OK==0, we don't need it
user32.MessageBox(nil, "Hello World!", "Lua", win32.MB_ICONERROR)
user32.MessageBox(nil, "Hello World!", "Lua", 'MB_ICONERROR')

That way when adding new bindings it's easy on the C side (I pick the
helper function that I need), and from the Lua side the user chooses
the style he likes.

The only limitation is that you cannot use the win32.CST_A +
win32.CST_B syntax if any of these two constants don't fit the integer
part of a lua_Number, because in that case I would expose them
directly as light userdata (which fit all enums and flags I've met so
far).