-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathlibwrapper.lua
More file actions
138 lines (123 loc) · 5.32 KB
/
libwrapper.lua
File metadata and controls
138 lines (123 loc) · 5.32 KB
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
--[[ next hurdle
I'm getting too many 'table overflow's
and we all know luajit ffi doesn't handle scope at all (sad)
so how to keep the perf benefits of a ffi.C.ENUM_VALUE or a ffi.c.funcCall() ,but also the flexibility of porting an entire .h file into luajit-ffi ?
how about an API for requesting enums/typedefs/functions, so only the ones that are used can be defined?
I fear/suspect this will start to incorporate my `modules` library...
- First call `require 'ffi.req' 'c-header.lua'` to prepare all symbols to be requested.
- Second call `require 'ffi'.enum'ENUM_NAME'` to define only the requested names into ffi.C.
hmm,
alternatively I could overload the library table themselves.
or the table returned by `require 'ffi.req'` could have its __index overloaded to generate-upon-request ...
... but that would be a performance hit, since it isn't overloading a Lua table (which , upon retrieval and assignment, would bypass the __index)
... but instead is overloading a table that itself woudl have to be__index'd back to the ffi.C table ...
or
I could make it optional per-application,
where choosing 'defer' incurs perf penalties (but allows more symbols to be loaded)
whlie choosing 'immediate' loads everything-per-header with no __index overloading, for faster access, but risks more 'table overflow's
--]]
local assert = require 'ext.assert'
local op = require 'ext.op'
local ffi = require 'ffi'
local M = {}
-- for now I'll defer-by-default, combined with only using ffi.libwrapper with slow/offline libs like libpng/cfitsio/libtiff that I don't call in tight loops
--M.mode = 'immediate' -- load immediately into ffi.C[k]
--M.mode = 'defer' -- defer loading ffi.C[k] until requested
M.mode = 'defer-lua' -- defer loading wrapper[k] until requested
--[[
args:
defs = {[k] = gen()} key is symbol/enum name,
... number values represent enums, to-be-defined in either the lib / ffi.C or in the wrapper (based on M.mode)
... string values represent ffi.cdef's
... function values are executed. these are useful for subsequent invocations / generating dependent types before ffi.cdef'ing our own type.
(TODO JUST USE THE MODULE SYSTEM. THE DAG AND ffi.cdef CALLS ARE ALREADY THERE.)
lib = `require 'ffi.load' (libraryName)` optional, if omitted this defaults to `ffi.C`.
init = {[k] = v}, optional, default {}
... this is the wrapper itself.
... put any key/values you want to initialize the wrapper with in here.
... whereas defs holds generators to be called upon first __index, this holds values always initialized from the start
creates a wrapper for the library based on `require 'ffi.libwrapper'.mode`
- if it is 'immediate' then all enums are immediately loaded into the library (this can only be assigned / used if M.mode is set to 'immediate' before require'ing the ffi.req header name.
- if it is 'defer' then enums are only assigned upon request, and assigned to ffi.C
- if it is 'defer-lua' then " " " assigned to the wrapper table
--]]
function M.libwrapper(args)
-- TODO this is run in require() at file-scope, so if it errors then you might get a "loop or previous error when requiring" error ...
-- Should I just be stderr'ing the errors?
-- Should I be fixing `require` to do that for all require'd files?
local defs = assert.type(args.defs or {}, 'table')
local lib = ffi.C
if args.lib ~= nil then
lib = assert.type(assert.index(args, 'lib'), 'userdata')
end
if M.mode == 'immediate' then
-- don't wrap
-- just define all into lib
-- this can incur 'table overflow'
-- but should be fast since the returned object is lib / ffi.C
-- TODO for immediate mode, I could always just assign __index=lib directly
-- however this would make things a lot more rigid, it'd make it not possible to change away from immediate mode to any other mode after launching an app (tho who will do that?)
for k,v in pairs(defs) do
--DEBUG(ffi.libwrapper): print('libwrapper loading', k)
if type(v) == 'number' then
ffi.cdef('enum { '..k..' = '..v..' };')
elseif type(v) == 'string' then
ffi.cdef(v)
elseif type(v) == 'function' then
-- TODO ... this just won't work.
-- functions-as-generators can't write to lib
-- which means they won't work with mode == immediate
wrapper[k] = v()
else
error("expected defs type to be number or string")
end
end
return lib
elseif M.mode == 'defer'
or M.mode == 'defer-lua'
then
-- do wrap -- and incur slowdowns
local wrapper = args.init or {}
setmetatable(wrapper, {
__index = function(t,k)
local v = op.safeindex(lib, k)
if v ~= nil then
t[k] = v
return v
end
local v = defs[k]
if v ~= nil then
--DEBUG(ffi.libwrapper): print('libwrapper loading', k)
if type(v) == 'number' then
if M.mode == 'defer' then
ffi.cdef('enum { '..k..' = '..v..' };')
t[k] = v
elseif M.mode == 'defer-lua' then
t[k] = v
end
return v
elseif type(v) == 'string' then
ffi.cdef(v)
-- it should be there by now ...
local result = op.safeindex(lib, k)
t[k] = result
return result
elseif type(v) == 'function' then
local result = v()
t[k] = result
return result
else
error("expected defs type to be number or string")
end
end
end,
})
return wrapper
end
end
setmetatable(M, {
__call = function(t,...)
return M.libwrapper(...)
end,
})
return M