xtype (halted)

Check-in [1f1ab704d5]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Improve cdata support with __xtype field.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 1f1ab704d55f37b5d61bc8abb8ba2b9ff9b133b3ced3665aa2fcb77813ad8fe9
User & Date: imagic 2021-11-10 00:55:08.000
Context
2021-11-10
19:31
Improve tests and add global test. ... (check-in: 786267d6c7 user: imagic tags: trunk)
00:55
Improve cdata support with __xtype field. ... (check-in: 1f1ab704d5 user: imagic tags: trunk)
2021-11-08
20:25
Add low-level API. ... (check-in: be91a3e5fe user: imagic tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to README.adoc.
28
29
30
31
32
33
34
35






36
37
38
39
40
41
42

A primitive type is a string. It can be any Lua types from `type()` or additional types like `xtype` or `multifunction`.

A non-primitive type is a table defining the type. It contains the `xtype_name`, `xtype_stack` and `xtype_set` fields and should identify as a `xtype`.

==== Value

Any Lua value has a type; we will call this the terminal type of the value as opposed to inherited types. If the metatable of a `table` or `userdata` value has a `xtype` field, then the type is the value of that field. If the value is a `cdata`, its type is defined by `xtype.ctype()`. Otherwise, the type is the Lua type returned by `type()`.







==== Inheritance

A non-primitive type can inherit from other types, building a stack of types. This stack defines the order in which the types are evaluated, from the terminal type to the least specific inherited types.

=== Multifunction








|
>
>
>
>
>
>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

A primitive type is a string. It can be any Lua types from `type()` or additional types like `xtype` or `multifunction`.

A non-primitive type is a table defining the type. It contains the `xtype_name`, `xtype_stack` and `xtype_set` fields and should identify as a `xtype`.

==== Value

Any Lua value has a type; we will call this the terminal type of the value as opposed to inherited types.

If the metatable of a `table` or `userdata` value has a `xtype` field, then the type is the value of that field.

If the value is a `cdata`, its type is defined by `xtype.ctype()` or the `\__xtype` field (e.g. through the metatype `__index`) which will call `xtype.ctype()` when encountered.

Otherwise, the type is the Lua type returned by `type()`.

==== Inheritance

A non-primitive type can inherit from other types, building a stack of types. This stack defines the order in which the types are evaluated, from the terminal type to the least specific inherited types.

=== Multifunction

Changes to examples/test_cdata.lua.
1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43












-- Test FFI cdata support.
package.path = "src/?.lua;"..package.path
local ffi = require("ffi")
local xtype = require("xtype")


ffi.cdef("typedef struct{double x, y;} vec2_t;")
local vec2 = xtype.create("vec2")
local vec2_t = ffi.typeof("vec2_t")
-- type behavior
setmetatable(vec2, {
  xtype = "xtype",
  __call = function(_, ...) return vec2_t(...) end
})
-- instance behavior
ffi.metatype(vec2_t, {
  __unm = function(v) return vec2(-v.x, -v.y) end,
  __add = xtype.op.add,
  __sub = xtype.op.sub,
  __mul = xtype.op.mul,
  __div = xtype.op.div,
  __eq = xtype.op.eq
})
xtype.op.add:define(function(a, b) return vec2(a.x+b.x, a.y+b.y) end, vec2, vec2)
xtype.op.mul:define(function(v, n) return vec2(v.x*n, v.y*n) end, vec2, "number")
xtype.op.mul:define(function(n, v) return vec2(v.x*n, v.y*n) end, "number", vec2)
xtype.op.div:define(function(a, b) return vec2(a.x/b.x, a.y/b.y) end, vec2, vec2)
xtype.op.div:define(function(v, n) return vec2(v.x/n, v.y/n) end, vec2, "number")
xtype.op.eq:define(function(a, b) return a.x == b.x and a.y == b.y end, vec2, vec2)
-- bind
xtype.ctype(vec2_t, vec2)

assert(vec2(1,1) == vec2(1,1))
assert(vec2(1,1)*2 == 2*vec2(1,1))
assert(vec2(1,1)+vec2(1,1) == vec2(4,4)/2)

local a = ffi.new("vec2_t[1]", {{2,2}})
local r = a[0]
local p = a+0
xtype.ctype(ffi.typeof(r), vec2)
xtype.ctype(ffi.typeof(p), vec2)
assert(r == p)
assert(r == vec2(2,2))
assert(r/p == vec2(1,1))

















>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|
|
<
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

32
33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
-- Test FFI cdata support.
package.path = "src/?.lua;"..package.path
local ffi = require("ffi")
local xtype = require("xtype")

do -- test vec example
  ffi.cdef("typedef struct{double x, y;} vec2_t;")
  local vec2 = xtype.create("vec2")
  local vec2_t = ffi.typeof("vec2_t")
  -- type behavior
  setmetatable(vec2, {
    xtype = "xtype",
    __call = function(_, ...) return vec2_t(...) end
  })
  -- instance behavior
  ffi.metatype(vec2_t, {
    __unm = function(v) return vec2(-v.x, -v.y) end,
    __add = xtype.op.add,
    __sub = xtype.op.sub,
    __mul = xtype.op.mul,
    __div = xtype.op.div,
    __eq = xtype.op.eq
  })
  xtype.op.add:define(function(a, b) return vec2(a.x+b.x, a.y+b.y) end, vec2, vec2)
  xtype.op.mul:define(function(v, n) return vec2(v.x*n, v.y*n) end, vec2, "number")
  xtype.op.mul:define(function(n, v) return vec2(v.x*n, v.y*n) end, "number", vec2)
  xtype.op.div:define(function(a, b) return vec2(a.x/b.x, a.y/b.y) end, vec2, vec2)
  xtype.op.div:define(function(v, n) return vec2(v.x/n, v.y/n) end, vec2, "number")
  xtype.op.eq:define(function(a, b) return a.x == b.x and a.y == b.y end, vec2, vec2)
  -- bind
  xtype.ctype(vec2_t, vec2)

  assert(vec2(1,1) == vec2(1,1))
  assert(vec2(1,1)*2 == 2*vec2(1,1))
  assert(vec2(1,1)+vec2(1,1) == vec2(4,4)/2)

  local a = ffi.new("vec2_t[1]", {{2,2}})
  local r = a[0]
  local p = a+0
  xtype.ctype(ffi.typeof(r), vec2)
  xtype.ctype(ffi.typeof(p), vec2)
  assert(r == p)
  assert(r == vec2(2,2))
  assert(r/p == vec2(1,1))
end
do -- test type acquisition from field
  ffi.cdef("typedef struct{} test_field_t;")
  local test_field = xtype.create("test_field")
  local ctype = ffi.typeof("test_field_t")
  ffi.metatype(ctype, {__index = {__xtype = test_field}})
  -- checks
  assert(xtype.get(ffi.new("test_field_t")) == test_field)
  assert(xtype.get(ffi.new("test_field_t*")) == test_field)
  local ar = ffi.new("test_field_t[1]")
  assert(xtype.get(ar[1]) == test_field)
end
Changes to src/xtype.lua.
91
92
93
94
95
96
97
98





99
100
101
102
103
104
105
-- Get terminal type of a value.
local function xtype_get(v)
  local v_type = type(v)
  if v_type == "table" or v_type == "userdata" then
    local mt = getmetatable(v)
    return mt and mt.xtype or v_type
  elseif v_type == "cdata" then
    return ctype_types[tonumber(ffi.typeof(v))] or v_type





  else return v_type end
end
xtype.get = xtype_get

-- Check if a value is of type t.
xtype_is = function(v, t)
  check_type(t, 2)







|
>
>
>
>
>







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
-- Get terminal type of a value.
local function xtype_get(v)
  local v_type = type(v)
  if v_type == "table" or v_type == "userdata" then
    local mt = getmetatable(v)
    return mt and mt.xtype or v_type
  elseif v_type == "cdata" then
    local xt = ctype_types[tonumber(ffi.typeof(v))]
    if not xt then -- try to acquire type from field
      local ok; ok, xt = pcall(function(v) return v.__xtype end, v)
      if ok then xtype.ctype(ffi.typeof(v), xt) end
    end
    return xt or v_type
  else return v_type end
end
xtype.get = xtype_get

-- Check if a value is of type t.
xtype_is = function(v, t)
  check_type(t, 2)