Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Rework enums and bitflags handling

They are now primarily trwated as string or sets of strings, instead of numbers.
  • Loading branch information...
commit d19244b4e403f9386d7f9576e89086ea4c1cce3b 1 parent 7d40c1c
@pavouk authored
View
7 README.md
@@ -43,7 +43,14 @@ markdown processor if you want to read it in HTML.
### 0.4 (unreleased)
+- Changed handling of enums and bitflags, switched from marshaling
+ them as numbers to prefering strings for enums and tables (sets or
+ lists) for bitflags. Numeric values still work for Lua->C
+ marshalling, but backward compatibility is broken in C->Lua enum and
+ bitflags marshalling.
- Compatible with Lua 5.2 and LuaJIT
+- Added standardized way for overrides to handle constructor argument
+ table array part.
- Existing Gtk overrides reworked and improved, there is now a way to
describe and create widget hierarchies in Lua-friendly way. See
`docs/gtk.lua`, chapter about `Gtk.Container` for overview and
View
61 docs/guide.md
@@ -61,6 +61,11 @@ mapping between GLib types and Lua types is established.
* `gboolean` is mapped to Lua's `boolean` type, with `true` and
`false` values
* All numeric types are mapped to Lua's `number` type
+* Enumerations are primarily handled as strings with uppercased GType
+ nicks, optionally the direct numeric values are also accepted.
+* Bitflags are primarily handled as lists or sets of strings with
+ uppercased GType nicks, optionally the direct numeric values are
+ also accepted.
* `gchar*` string is mapped to Lua as `string` type, UTF-8 encoded
* C array types and `GArray` is mapped to Lua tables, using array part
of the table. Note that although in C the arrays are 0-based, when
@@ -440,11 +445,22 @@ Fields are accessed using `.` operator on structure instance, for example
## 5. Enums and bitflags, constants
-Enum instances are represented as plain numbers in LGI. So in any
-place where enum or bitflags instance is needed, a number can be used
-directly instead.
+LGI primarily maps enumerations to strings containing uppercased nicks
+of enumeration constant names. Optionally, a direct enumeration value
+is also accepted. Similarly, bitflags are primarily handled as sets
+containing uppercased flag nicks, but also lists of these nicks or
+direct numeric value is accepted. When a numeric value cannot be
+mapped cleanly to the known set of bitflags, the remaining number is
+stored in the first array slot of the returned set.
-### 5.1. Accessing values
+Note that this behavior changed in lgi 0.4; up to that alpha release,
+lgi handled enums and bitmaps exclusively as numbers only. The change
+is compatible in Lua->C direction, where numbers still can be used,
+but incompatible in C->Lua direction, where lgi used to return
+numbers, while now it returns either string with enum value or table
+with flags.
+
+### 5.1. Accessing numeric values
In order to retrieve real enum values from symbolic names, enum and
bitflags are loaded into repository as tables mapping symbolic names
@@ -459,7 +475,8 @@ yields following output:
};
so constants can be referenced using `Gtk.WindowType.TOPLEVEL`
-construct.
+construct, or directly using string `'TOPLEVEL'` when a
+`Gtk.WindowType` is expected.
### 5.2. Backward mapping, getting names from numeric values
@@ -493,6 +510,40 @@ all symbolic names which make up the requested value:
SORTED = 32;
ODD = 2;
};
+
+This way, it is possible to check for presence of specified flag very
+easily:
+
+ if Gtk.RegionFlags[flags].ODD then
+ -- Code handling region-odd case
+ endif
+
+If the value cannot be cleanly decomposed to known flags, remaining
+bits are accumulated into number stored at index 1:
+
+> dump(Gtk.RegionFlags[51])
+
+ ["table: 0x242fb20"] = { -- table: 0x242fb20
+ EVEN = 1;
+ SORTED = 32;
+ [1] = 16;
+ ODD = 2;
+ };
+
+To construct numeric value which can be passed to a function expecting
+an enum, it is possible to simply add requested flags. However, there
+is a danger if some definition contains multiple flags , in which case
+numeric adding produces incorrect results. Therefore, it is possible
+to use bitflags pseudoconstructor', which accepts table containing
+requested flags:
+
+> =Gtk.RegionFlags { 'FIRST', 'SORTED' }
+
+ 36
+
+> =Gtk.RegionFlags { Gtk.RegionFlags.ODD, 16, 'EVEN' }
+
+ 19
## 6. Threading and synchronization
View
89 lgi/enum.lua
@@ -13,6 +13,37 @@ local core = require 'lgi.core'
local gi = core.gi
local component = require 'lgi.component'
+-- Prepare needed bit operations. Prefer bit32 C module if available,
+-- but if it is not, use poor-man Lua-only variants.
+local bor, has_bit
+local ok, bitlib = pcall(require, 'bit32')
+if ok then
+ -- Lua 5.2 style bit operations.
+ bor, has_bit = bitlib.bor, bitlib.btest
+else
+ ok, bitlib = pcall(require, 'bit')
+ if ok then
+ -- LuaBitOp package.
+ bor, has_bit = bitlib.bor, bitlib.band
+ else
+ -- Poor-man's Lua-only implementation, slow but out-of-the-box
+ -- for any kind of Lua.
+ function has_bit(value, bitmask)
+ return value % (2 * bitmask) >= bitmask
+ end
+ local function bor(o1, o2)
+ local res, bit = 0, 1
+ while bit <= o1 or bit <= o2 do
+ if has_bit(o1, bit) or has_bit(o2, bit) then
+ res = res + bit
+ end
+ bit = bit * 2
+ end
+ return res
+ end
+ end
+end
+
local enum = {
enum_mt = component.mt:clone { '_method' },
bitflags_mt = component.mt:clone { '_method' }
@@ -29,8 +60,9 @@ function enum.load(info, meta)
local prefix = info.name:gsub('%u+[^%u]+', '%1_'):lower()
local namespace = core.repo[info.namespace]
enum_type._method = setmetatable(
- {},
- { __index = function(_, name) return namespace[prefix .. name] end })
+ {}, { __index = function(_, name)
+ return namespace[prefix .. name]
+ end })
end
-- Load all enum values.
@@ -47,26 +79,55 @@ end
-- Enum reverse mapping, value->name.
function enum.enum_mt:_element(instance, value)
- local element, category = component.mt._element(self, instance, value)
- if element then return element, category end
- for name, val in pairs(self) do
- if val == value then return name end
+ if type(value) == 'number' then
+ for name, val in pairs(self) do
+ if val == value then return name end
+ end
+ return value
+ else
+ return component.mt._element(self, instance, value)
end
end
+-- Constructs enum number from specified string.
+function enum.enum_mt:_new(param)
+ if type(param) == 'string' then param = self[param] end
+ return param
+end
+
-- Resolving arbitrary number to the table containing symbolic names
-- of contained bits.
function enum.bitflags_mt:_element(instance, value)
- local element, category = component.mt._element(self, instance, value)
- if element then return element, category end
- if type(value) ~= 'number' then return end
- local result = {}
- for name, flag in pairs(self) do
- if type(flag) == 'number' and core.has_bit(value, flag) then
- result[name] = flag
+ if type(value) == 'number' then
+ local result, remainder = {}, value
+ for name, flag in pairs(self) do
+ if type(flag) == 'number' and has_bit(value, flag) then
+ result[name] = true
+ remainder = remainder - flag
+ end
+ end
+ if remainder > 0 then result[1] = remainder end
+ return result
+ else
+ return component.mt._element(self, instance, value)
+ end
+end
+
+-- 'Constructs' number from specified flags (or accepts just number).
+function enum.bitflags_mt:_new(param)
+ if type(param) == 'string' then
+ return self[param]
+ elseif type(param) == 'number' then
+ return param
+ else
+ local num = 0
+ for key, value in pairs(param) do
+ if type(key) == 'string' then value = key end
+ if type(value) == 'string' then value = self[value] end
+ num = bor(num, value)
end
+ return num
end
- return result
end
return enum
View
6 lgi/init.lua
@@ -19,12 +19,6 @@ local core = require 'lgi.core'
-- Create lgi table, containing the module.
local lgi = { _NAME = 'lgi', _VERSION = require 'lgi.version' }
--- Add simple flag-checking function, avoid compatibility hassle with
--- importing bitlib just because of this simple operation.
-function core.has_bit(value, flag)
- return value % (2 * flag) >= flag
-end
-
-- Forward 'yield' functionality into external interface.
lgi.yield = core.yield
View
21 lgi/marshal.c
@@ -826,6 +826,16 @@ lgi_marshal_2c (lua_State *L, GITypeInfo *ti, GIArgInfo *ai,
{
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
+ /* If the argument is not numeric, convert to number
+ first. Use enum/flags 'constructor' to do this. */
+ if (lua_type (L, narg) != LUA_TNUMBER)
+ {
+ lgi_type_get_repotype (L, G_TYPE_INVALID, info);
+ lua_pushvalue (L, narg);
+ lua_call (L, 1, 1);
+ narg = -1;
+ }
+
/* Directly store underlying value. */
marshal_2c_int (L, g_enum_info_get_storage_type (info), arg, narg,
optional, FALSE);
@@ -1094,9 +1104,18 @@ lgi_marshal_2lua (lua_State *L, GITypeInfo *ti, GITransfer transfer,
{
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
- /* Directly store underlying value. */
+ /* Prepare repotable of enum/flags on the stack. */
+ lgi_type_get_repotype (L, G_TYPE_INVALID, info);
+
+ /* Unmarshal the numeric value. */
marshal_2lua_int (L, g_enum_info_get_storage_type (info),
arg, FALSE);
+
+ /* Get symbolic value from the table. */
+ lua_gettable (L, -2);
+
+ /* Remove the table from the stack. */
+ lua_remove (L, -2);
break;
case GI_INFO_TYPE_STRUCT:
View
5 lgi/override/Clutter.lua
@@ -40,7 +40,6 @@ Clutter.threads_init()
-- Automatically initialize clutter, avoid continuing if
-- initialization fails.
local status = Clutter.init()
-if status ~= Clutter.InitError.SUCCESS then
- error(("Clutter initialization failed: %s"):format(
- Clutter.InitError(status)))
+if status ~= 'SUCCESS' then
+ error(("Clutter initialization failed: %s"):format(status))
end
View
5 lgi/override/GObject-Object.lua
@@ -128,7 +128,7 @@ end
local function marshal_property(obj, name, flags, gtype, marshaller, ...)
-- Check access rights of the property.
local mode = select('#', ...) > 0 and 'WRITABLE' or 'READABLE'
- if not core.has_bit(flags, repo.GObject.ParamFlags[mode]) then
+ if not flags[mode] then
error(("%s: `%s' not %s"):format(core.object.query(obj, 'repo')._name,
name, mode:lower()))
end
@@ -147,7 +147,8 @@ function Object:_access_property(object, property, ...)
local typeinfo = property.typeinfo
local gtype = Type.from_typeinfo(typeinfo)
local marshaller = Value.find_marshaller(gtype, typeinfo, property.transfer)
- return marshal_property(object, property.name, property.flags,
+ return marshal_property(object, property.name,
+ repo.GObject.ParamFlags[property.flags],
gtype, marshaller, ...)
end
View
9 samples/console.lua
@@ -283,11 +283,10 @@ local function Console()
function entry:on_key_press_event(event)
-- Lookup action to be activated for specified key combination.
local action = keytable[event.keyval]
- local mask = Gdk.ModifierType[event.state]
- local wants_control = actions.multiline.active
- and Gdk.ModifierType.CONTROL_MASK or nil
- if not action or mask.SHIFT_MASK
- or mask.CONTROL_MASK ~= wants_control then
+ local state = event.state
+ local without_control = not state.CONTROL_MASK
+ if not action or state.SHIFT_MASK
+ or actions.multiline.active == without_control then
return false
end
View
60 tests/gireg.lua
@@ -755,7 +755,7 @@ function gireg.enum()
check(R.TestEnum[0] == 'VALUE1')
check(R.TestEnum[1] == 'VALUE2')
check(R.TestEnum[-1] == 'VALUE3')
- check(R.TestEnum[43] == nil)
+ check(R.TestEnum[43] == 43)
check(R.test_enum_param(0) == 'value1')
check(R.test_enum_param(1) == 'value2')
check(R.test_enum_param(-1) == 'value3')
@@ -764,7 +764,7 @@ function gireg.enum()
check(R.TestEnumUnsigned.VALUE2 == 0x80000000)
check(R.TestEnumUnsigned[1] == 'VALUE1')
check(R.TestEnumUnsigned[0x80000000] == 'VALUE2')
- check(R.TestEnumUnsigned[-1] == nil)
+ check(R.TestEnumUnsigned[-1] == -1)
end
function gireg.flags()
@@ -772,12 +772,28 @@ function gireg.flags()
check(R.TestFlags.FLAG1 == 1)
check(R.TestFlags.FLAG2 == 2)
check(R.TestFlags.FLAG3 == 4)
- check(R.TestFlags[7].FLAG1 == 1)
- check(R.TestFlags[7].FLAG2 == 2)
- check(R.TestFlags[7].FLAG3 == 4)
- check(R.TestFlags[3].FLAG1 == 1)
- check(R.TestFlags[3].FLAG2 == 2)
+ check(R.TestFlags[7].FLAG1 == true)
+ check(R.TestFlags[7].FLAG2 == true)
+ check(R.TestFlags[7].FLAG3 == true)
+ check(R.TestFlags[3].FLAG1 == true)
+ check(R.TestFlags[3].FLAG2 == true)
check(R.TestFlags[3].FLAG3 == nil)
+ check(R.TestFlags[10].FLAG2 == true)
+ check(R.TestFlags[10][1] == 8)
+ checkv(R.TestFlags { 'FLAG1', 'FLAG2' }, 3, 'number')
+ checkv(R.TestFlags { 1, 2, 'FLAG1', R.TestFlags.FLAG2 }, 3, 'number')
+ checkv(R.TestFlags { 10, FLAG2 = 2 }, 10, 'number')
+ checkv(R.TestFlags { 2, 'FLAG2' }, 2, 'number')
+end
+
+function gireg.flags_out()
+ local R = lgi.Regress
+ local out = R.global_get_flags_out()
+ check(type(out) == 'table')
+ check(out.FLAG1 == true)
+ check(out.FLAG2 == nil)
+ check(out.FLAG3 == true)
+ check(#out == 0)
end
function gireg.const()
@@ -800,25 +816,25 @@ function gireg.struct_a()
a.some_double = 3.14
check(a.some_double == 3.14)
a.some_enum = R.TestEnum.VALUE2
- check(a.some_enum == R.TestEnum.VALUE2)
+ check(a.some_enum == 'VALUE2')
a = R.TestStructA { some_int = 42, some_int8 = 12,
- some_double = 3.14, some_enum = R.TestEnum.VALUE2 }
+ some_double = 3.14, some_enum = 'VALUE2' }
a.some_int = 43
a.some_int8 = 13
check(a.some_int == 43)
check(a.some_int8 == 13)
check(a.some_double == 3.14)
- check(a.some_enum == R.TestEnum.VALUE2)
+ check(a.some_enum == 'VALUE2')
a.some_double = 3.15
check(a.some_int == 43)
check(a.some_int8 == 13)
check(a.some_double == 3.15)
- check(a.some_enum == R.TestEnum.VALUE2)
+ check(a.some_enum == 'VALUE2')
a.some_enum = R.TestEnum.VALUE3
check(a.some_int == 43)
check(a.some_int8 == 13)
check(a.some_double == 3.15)
- check(a.some_enum == R.TestEnum.VALUE3)
+ check(a.some_enum == 'VALUE3')
check(not pcall(function() return a.foo end))
check(not pcall(function() a.foo = 1 end))
check(select('#', (function() a.some_int = 0 end)()) == 0)
@@ -839,11 +855,11 @@ function gireg.struct_a_clone()
check(b.some_int == 42)
check(b.some_int8 == 12)
check(b.some_double == 3.14)
- check(b.some_enum == R.TestEnum.VALUE2)
+ check(b.some_enum == 'VALUE2')
check(a.some_int == 42)
check(a.some_int8 == 12)
check(a.some_double == 3.14)
- check(a.some_enum == R.TestEnum.VALUE2)
+ check(a.some_enum == 'VALUE2')
end
function gireg.struct_b()
@@ -867,7 +883,7 @@ function gireg.struct_b()
check(b.nested_a.some_int == 42)
check(b.nested_a.some_int8 == 12)
check(b.nested_a.some_double == 3.14)
- check(b.nested_a.some_enum == R.TestEnum.VALUE2)
+ check(b.nested_a.some_enum == 'VALUE2')
-- Nested structure construction.
b = R.TestStructB { some_int8 = 21, nested_a =
@@ -877,7 +893,7 @@ function gireg.struct_b()
check(b.nested_a.some_int == 42)
check(b.nested_a.some_int8 == 12)
check(b.nested_a.some_double == 3.14)
- check(b.nested_a.some_enum == R.TestEnum.VALUE2)
+ check(b.nested_a.some_enum == 'VALUE2')
end
function gireg.struct_b_clone()
@@ -896,21 +912,21 @@ function gireg.struct_b_clone()
check(bc.nested_a.some_int == 42)
check(bc.nested_a.some_int8 == 12)
check(bc.nested_a.some_double == 3.14)
- check(bc.nested_a.some_enum == R.TestEnum.VALUE2)
+ check(bc.nested_a.some_enum == 'VALUE2')
check(bc.nested_a.some_int == 42)
check(bc.nested_a.some_int8 == 12)
check(bc.nested_a.some_double == 3.14)
- check(bc.nested_a.some_enum == R.TestEnum.VALUE2)
+ check(bc.nested_a.some_enum == 'VALUE2')
check(b.some_int8 == 21)
check(b.nested_a.some_int == 42)
check(b.nested_a.some_int8 == 12)
check(b.nested_a.some_double == 3.14)
- check(b.nested_a.some_enum == R.TestEnum.VALUE2)
+ check(b.nested_a.some_enum == 'VALUE2')
check(b.nested_a.some_int == 42)
check(b.nested_a.some_int8 == 12)
check(b.nested_a.some_double == 3.14)
- check(b.nested_a.some_enum == R.TestEnum.VALUE2)
+ check(b.nested_a.some_enum == 'VALUE2')
end
function gireg.boxed_a_equals()
@@ -1058,14 +1074,14 @@ function gireg.gvalue_date()
local v = R.test_date_in_gvalue()
check(v.gtype == 'GDate')
check(v.value:get_day() == 5)
- check(v.value:get_month() == 12)
+ check(v.value:get_month() == 'DECEMBER')
check(v.value:get_year() == 1984)
local d = GLib.Date()
d:set_dmy(25, 1, 1975)
v = GObject.Value(GLib.Date, d)
check(v.gtype == 'GDate')
check(v.value:get_day() == 25)
- check(v.value:get_month() == 1)
+ check(v.value:get_month() == 'JANUARY')
check(v.value:get_year() == 1975)
end
Please sign in to comment.
Something went wrong with that request. Please try again.