lua-users home
lua-l archive

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


Hello!

The term enum leads to the association with luaL_checkoption().  This is
practical for potentially long lists of strings, which are asociated to
_some_ *different* integer value.  The specific integer value of a specific
enum identifier might not even be important.

The other use case, described by Ezequiel García in the initial post, is that
of a bitmask, shown here with a C enum, but also frequently practiced with
#defines in API headers.

Here the situation is different, in that each string 'means' a specific
bit out of a limited number (e.g. 32), and the combination of a specific
set of string identifiers results in bit-operated integer value with a
new specific meaning (while arithmetic combinations of enum identifiers
in the first sense are meaningless).

Often there are several different "bitmasks" in a programming system, which
map different set of strings to the same bits.  It would be nice to
be able to define the bitmask names in a context and to have a reverse map
function of any combination of bits to a list of strings in the respective
context.

Given this considerations I am trying to wrap my mind around an idea of a
small module which acomplishes this type of bitmask "enums" in Lua,
while not wasting neither CPU nor memory at runtime.

What I have in mind, is a "table"-kind thing, with exactly 32 (or
whatever the wordsize of your cpu has) "bit" keys: 1 .. 32.  Each
key can either hold a Lua string or nil.  If indexed via a string the
respective bitnumber is retrieved - if there is no bit with that name
an error is thrown.

With some auxiliary functions Ezequiel's example would transform to:

--
BufferMode = bitmask{
    [0]='UNKNOWN',    -- special case: all bits 0
    [1]='FRONTONLY',  -- 0x00000001
    [2]='BACKVIDEO',  -- 0x00000002
    [3]='BACKSYSTEM', -- 0x00000004
    [4]='TRIPLE',     -- 0x00000008
    [5]='WINDOWS',    -- 0x00000010
}

buffermode = BufferMode{'TRIPLE', 'WINDOWS'}
--


For Jerome's example something like the following would result:

--
win32 = {};
win32.MessageBoxFlags = bitmask{[0]='MB_OK', [5]='MB_ICONERROR' } -- inside some large module with hundreds of definitions.

user32.MessageBox(nil, "Hello World!", "Lua", win32.MessageBoxFlags{'MB_OK', 'MB_ICONERROR'})
--

Does this make sense?

Regards,

    Jorge-León


On 12/15/11 16:29, Jerome Vuarand wrote:
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).