forked from torch/cwrap
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcinterface.lua
308 lines (256 loc) · 8.29 KB
/
cinterface.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
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
local CInterface = {}
function CInterface.new()
self = {}
self.txt = {}
self.registry = {}
setmetatable(self, {__index=CInterface})
return self
end
function CInterface:luaname2wrapname(name)
return string.format("wrapper_%s", name)
end
function CInterface:print(str)
table.insert(self.txt, str)
end
function CInterface:wrap(luaname, ...)
local txt = self.txt
local varargs = {...}
assert(#varargs > 0 and #varargs % 2 == 0, 'must provide both the C function name and the corresponding arguments')
-- add function to the registry
table.insert(self.registry, {name=luaname, wrapname=self:luaname2wrapname(luaname)})
table.insert(txt, string.format("static int %s(lua_State *L)", self:luaname2wrapname(luaname)))
table.insert(txt, "{")
table.insert(txt, "int narg = lua_gettop(L);")
if #varargs == 2 then
local cfuncname = varargs[1]
local args = varargs[2]
local helpargs, cargs, argcreturned = self:__writeheaders(txt, args)
self:__writechecks(txt, args)
table.insert(txt, 'else')
table.insert(txt, string.format('luaL_error(L, "expected arguments: %s");', table.concat(helpargs, ' ')))
self:__writecall(txt, args, cfuncname, cargs, argcreturned)
else
local allcfuncname = {}
local allargs = {}
local allhelpargs = {}
local allcargs = {}
local allargcreturned = {}
table.insert(txt, "int argset = 0;")
for k=1,#varargs/2 do
allcfuncname[k] = varargs[(k-1)*2+1]
allargs[k] = varargs[(k-1)*2+2]
end
local argoffset = 0
for k=1,#varargs/2 do
allhelpargs[k], allcargs[k], allargcreturned[k] = self:__writeheaders(txt, allargs[k], argoffset)
argoffset = argoffset + #allargs[k]
end
for k=1,#varargs/2 do
self:__writechecks(txt, allargs[k], k)
end
table.insert(txt, 'else')
local allconcathelpargs = {}
for k=1,#varargs/2 do
table.insert(allconcathelpargs, table.concat(allhelpargs[k], ' '))
end
table.insert(txt, string.format('luaL_error(L, "expected arguments: %s");', table.concat(allconcathelpargs, ' | ')))
for k=1,#varargs/2 do
if k == 1 then
table.insert(txt, string.format('if(argset == %d)', k))
else
table.insert(txt, string.format('else if(argset == %d)', k))
end
table.insert(txt, '{')
self:__writecall(txt, allargs[k], allcfuncname[k], allcargs[k], allargcreturned[k])
table.insert(txt, '}')
end
table.insert(txt, 'return 0;')
end
table.insert(txt, '}')
table.insert(txt, '')
end
function CInterface:register(name)
local txt = self.txt
table.insert(txt, string.format('static const struct luaL_Reg %s [] = {', name))
for _,reg in ipairs(self.registry) do
table.insert(txt, string.format('{"%s", %s},', reg.name, reg.wrapname))
end
table.insert(txt, '{NULL, NULL}')
table.insert(txt, '};')
table.insert(txt, '')
self.registry = {}
end
function CInterface:clearhistory()
self.txt = {}
self.registry = {}
end
function CInterface:tostring()
return table.concat(self.txt, '\n')
end
function CInterface:tofile(filename)
local f = io.open(filename, 'w')
f:write(table.concat(self.txt, '\n'))
f:close()
end
local function bit(p)
return 2 ^ (p - 1) -- 1-based indexing
end
local function hasbit(x, p)
return x % (p + p) >= p
end
local function beautify(txt)
local indent = 0
for i=1,#txt do
if txt[i]:match('}') then
indent = indent - 2
end
if indent > 0 then
txt[i] = string.rep(' ', indent) .. txt[i]
end
if txt[i]:match('{') then
indent = indent + 2
end
end
end
local function tableinsertcheck(tbl, stuff)
if stuff and not stuff:match('^%s*$') then
table.insert(tbl, stuff)
end
end
function CInterface:__writeheaders(txt, args, argoffset)
local argtypes = self.argtypes
local helpargs = {}
local cargs = {}
local argcreturned
argoffset = argoffset or 0
for i,arg in ipairs(args) do
arg.i = i+argoffset
arg.args = args -- in case we want to do stuff depending on other args
assert(argtypes[arg.name], 'unknown type ' .. arg.name)
setmetatable(arg, {__index=argtypes[arg.name]})
arg.__metatable = argtypes[arg.name]
tableinsertcheck(txt, arg:declare())
local helpname = arg:helpname()
if arg.returned then
helpname = string.format('*%s*', helpname)
end
if arg.invisible and arg.default == nil then
error('Invisible arguments must have a default! How could I guess how to initialize it?')
end
if arg.default ~= nil then
if not arg.invisible then
table.insert(helpargs, string.format('[%s]', helpname))
end
elseif not arg.creturned then
table.insert(helpargs, helpname)
end
if arg.creturned then
if argcreturned then
error('A C function can only return one argument!')
end
if arg.default ~= nil then
error('Obviously, an "argument" returned by a C function cannot have a default value')
end
if arg.returned then
error('Options "returned" and "creturned" are incompatible')
end
argcreturned = arg
else
table.insert(cargs, arg:carg())
end
end
return helpargs, cargs, argcreturned
end
function CInterface:__writechecks(txt, args, argset)
local argtypes = self.argtypes
local multiargset = argset
argset = argset or 1
local nopt = 0
for i,arg in ipairs(args) do
if arg.default ~= nil and not arg.invisible then
nopt = nopt + 1
end
end
for variant=0,math.pow(2, nopt)-1 do
local opt = 0
local currentargs = {}
local optargs = {}
local hasvararg = false
for i,arg in ipairs(args) do
if arg.invisible then
table.insert(optargs, arg)
elseif arg.default ~= nil then
opt = opt + 1
if hasbit(variant, bit(opt)) then
table.insert(currentargs, arg)
else
table.insert(optargs, arg)
end
elseif not arg.creturned then
table.insert(currentargs, arg)
end
end
for _,arg in ipairs(args) do
if arg.vararg then
if hasvararg then
error('Only one argument can be a "vararg"!')
end
hasvararg = true
end
end
if hasvararg and not currentargs[#currentargs].vararg then
error('Only the last argument can be a "vararg"')
end
local compop
if hasvararg then
compop = '>='
else
compop = '=='
end
if variant == 0 and argset == 1 then
table.insert(txt, string.format('if(narg %s %d', compop, #currentargs))
else
table.insert(txt, string.format('else if(narg %s %d', compop, #currentargs))
end
for stackidx, arg in ipairs(currentargs) do
table.insert(txt, string.format("&& %s", arg:check(stackidx)))
end
table.insert(txt, ')')
table.insert(txt, '{')
if multiargset then
table.insert(txt, string.format('argset = %d;', argset))
end
for stackidx, arg in ipairs(currentargs) do
tableinsertcheck(txt, arg:read(stackidx))
end
for _,arg in ipairs(optargs) do
tableinsertcheck(txt, arg:init())
end
table.insert(txt, '}')
end
end
function CInterface:__writecall(txt, args, cfuncname, cargs, argcreturned)
local argtypes = self.argtypes
for _,arg in ipairs(args) do
tableinsertcheck(txt, arg:precall())
end
if argcreturned then
table.insert(txt, string.format('%s = %s(%s);', argtypes[argcreturned.name].creturn(argcreturned), cfuncname, table.concat(cargs, ',')))
else
table.insert(txt, string.format('%s(%s);', cfuncname, table.concat(cargs, ',')))
end
for _,arg in ipairs(args) do
tableinsertcheck(txt, arg:postcall())
end
local nret = 0
if argcreturned then
nret = nret + 1
end
for _,arg in ipairs(args) do
if arg.returned then
nret = nret + 1
end
end
table.insert(txt, string.format('return %d;', nret))
end
return CInterface