├── CMakeLists.txt
├── COPYRIGHT.txt
├── README.md
├── cinterface.lua
├── doc
├── argumenttypes.md
├── example.md
├── highlevelinterface.md
├── index.md
└── usertypes.md
├── init.lua
├── mkdocs.yml
├── rocks
└── cwrap-scm-1.rockspec
└── types.lua
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR)
2 | CMAKE_POLICY(VERSION 2.6)
3 |
4 | SET(src "")
5 | SET(luasrc
6 | ${CMAKE_CURRENT_SOURCE_DIR}/init.lua
7 | ${CMAKE_CURRENT_SOURCE_DIR}/cinterface.lua
8 | ${CMAKE_CURRENT_SOURCE_DIR}/types.lua)
9 |
10 | INSTALL(FILES ${luasrc} DESTINATION ${LUADIR}/cwrap)
11 |
12 |
--------------------------------------------------------------------------------
/COPYRIGHT.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
2 | Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
3 | Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
4 | Copyright (c) 2011-2013 NYU (Clement Farabet)
5 | Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
6 | Copyright (c) 2006 Idiap Research Institute (Samy Bengio)
7 | Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
8 |
9 | All rights reserved.
10 |
11 | Redistribution and use in source and binary forms, with or without
12 | modification, are permitted provided that the following conditions are met:
13 |
14 | 1. Redistributions of source code must retain the above copyright
15 | notice, this list of conditions and the following disclaimer.
16 |
17 | 2. Redistributions in binary form must reproduce the above copyright
18 | notice, this list of conditions and the following disclaimer in the
19 | documentation and/or other materials provided with the distribution.
20 |
21 | 3. Neither the names of Deepmind Technologies, NYU, NEC Laboratories America
22 | and IDIAP Research Institute nor the names of its contributors may be
23 | used to endorse or promote products derived from this software without
24 | specific prior written permission.
25 |
26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 | POSSIBILITY OF SUCH DAMAGE.
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CWrap package #
2 |
3 | The __cwrap__ package helps you to automate the generation of Lua/C wrappers
4 | around existing C functions, such that these functions would be callable
5 | from Lua. This package is used by the __torch__ package, but does not depend on
6 | anything, and could be used by anyone using Lua.
7 | The documentation is organized as follows :
8 |
9 | * [Example Use Case](doc/example.md)
10 | * [High Level Interface](doc/highlevelinterface.md)
11 | * [Argument Types](doc/argumenttypes.md)
12 | * [User Types](doc/usertypes.md)
13 |
14 | __DISCLAIMER__ Before going any further, we assume the reader has a good
15 | knowledge of how to interface C functions with Lua. A good start would be
16 | the [Lua reference manual](http://www.lua.org/manual/5.1), or the book
17 | [Programming in Lua](http://www.inf.puc-rio.br/~roberto/pil2).
18 |
--------------------------------------------------------------------------------
/cinterface.lua:
--------------------------------------------------------------------------------
1 | local CInterface = {}
2 |
3 | function CInterface.new()
4 | self = {}
5 | self.txt = {}
6 | self.registry = {}
7 | self.defaultArguments = {}
8 | setmetatable(self, {__index=CInterface})
9 | return self
10 | end
11 |
12 | function CInterface:luaname2wrapname(name)
13 | return string.format("wrapper_%s", name)
14 | end
15 |
16 | function CInterface:print(str)
17 | table.insert(self.txt, str)
18 | end
19 |
20 | function CInterface:registerDefaultArgument(code)
21 | table.insert(self.defaultArguments, code)
22 | end
23 |
24 | function CInterface:wrap(luaname, ...)
25 | local txt = self.txt
26 | local varargs = {...}
27 |
28 | assert(#varargs > 0 and #varargs % 2 == 0, 'must provide both the C function name and the corresponding arguments')
29 |
30 | -- add function to the registry
31 | table.insert(self.registry, {name=luaname, wrapname=self:luaname2wrapname(luaname)})
32 |
33 | self:__addchelpers()
34 |
35 | table.insert(txt, string.format("static int %s(lua_State *L)", self:luaname2wrapname(luaname)))
36 | table.insert(txt, "{")
37 | table.insert(txt, "int narg = lua_gettop(L);")
38 |
39 | for i, defaultArgCode in ipairs(self.defaultArguments) do
40 | table.insert(txt, defaultArgCode(string.format("default_arg%d", i)))
41 | end
42 |
43 | if #varargs == 2 then
44 | local cfuncname = varargs[1]
45 | local args = varargs[2]
46 |
47 | local helpargs, cargs, argcreturned = self:__writeheaders(txt, args)
48 | self:__writechecks(txt, args)
49 |
50 | table.insert(txt, 'else')
51 | table.insert(txt, '{')
52 | table.insert(txt, string.format('char type_buf[512];'))
53 | table.insert(txt, string.format('str_arg_types(L, type_buf, 512);'))
54 | table.insert(txt, string.format('luaL_error(L, "invalid arguments: %%s\\nexpected arguments: %s", type_buf);',
55 | table.concat(helpargs, ' ')))
56 | table.insert(txt, '}')
57 |
58 | self:__writecall(txt, args, cfuncname, cargs, argcreturned)
59 | else
60 | local allcfuncname = {}
61 | local allargs = {}
62 | local allhelpargs = {}
63 | local allcargs = {}
64 | local allargcreturned = {}
65 |
66 | table.insert(txt, "int argset = 0;")
67 |
68 | for k=1,#varargs/2 do
69 | allcfuncname[k] = varargs[(k-1)*2+1]
70 | allargs[k] = varargs[(k-1)*2+2]
71 | end
72 |
73 | local argoffset = 0
74 | for k=1,#varargs/2 do
75 | allhelpargs[k], allcargs[k], allargcreturned[k] = self:__writeheaders(txt, allargs[k], argoffset)
76 | argoffset = argoffset + #allargs[k]
77 | end
78 |
79 | for k=1,#varargs/2 do
80 | self:__writechecks(txt, allargs[k], k)
81 | end
82 |
83 | table.insert(txt, 'else')
84 | local allconcathelpargs = {}
85 | for k=1,#varargs/2 do
86 | table.insert(allconcathelpargs, table.concat(allhelpargs[k], ' '))
87 | end
88 | table.insert(txt, '{')
89 | table.insert(txt, string.format('char type_buf[512];'))
90 | table.insert(txt, string.format('str_arg_types(L, type_buf, 512);'))
91 | table.insert(txt, string.format('luaL_error(L, "invalid arguments: %%s\\nexpected arguments: %s", type_buf);',
92 | table.concat(allconcathelpargs, ' | ')))
93 | table.insert(txt, '}')
94 |
95 | for k=1,#varargs/2 do
96 | if k == 1 then
97 | table.insert(txt, string.format('if(argset == %d)', k))
98 | else
99 | table.insert(txt, string.format('else if(argset == %d)', k))
100 | end
101 | table.insert(txt, '{')
102 | self:__writecall(txt, allargs[k], allcfuncname[k], allcargs[k], allargcreturned[k])
103 | table.insert(txt, '}')
104 | end
105 |
106 | table.insert(txt, 'return 0;')
107 | end
108 |
109 | table.insert(txt, '}')
110 | table.insert(txt, '')
111 | end
112 |
113 | function CInterface:__addchelpers()
114 | if not self.__chelpers_added then
115 | local txt = self.txt
116 | table.insert(txt, '#ifndef _CWRAP_STR_ARG_TYPES_4821726c1947cdf3eebacade98173939')
117 | table.insert(txt, '#define _CWRAP_STR_ARG_TYPES_4821726c1947cdf3eebacade98173939')
118 | table.insert(txt, '#include "string.h"')
119 | table.insert(txt, 'static void str_arg_types(lua_State *L, char *buf, int n) {')
120 | table.insert(txt, ' int i;')
121 | table.insert(txt, ' int nargs = lua_gettop(L);')
122 | table.insert(txt, ' if (nargs == 0) {')
123 | table.insert(txt, ' snprintf(buf, n, "no arguments provided");')
124 | table.insert(txt, ' return;')
125 | table.insert(txt, ' }')
126 | table.insert(txt, ' for (i = 1; i <= nargs; i++) {')
127 | table.insert(txt, ' int l;')
128 | table.insert(txt, ' const char *torch_type = luaT_typename(L, i);')
129 | table.insert(txt, ' if(torch_type && !strncmp(torch_type, "torch.", 6)) torch_type += 6;')
130 | table.insert(txt, ' if (torch_type) l = snprintf(buf, n, "%s ", torch_type);')
131 | table.insert(txt, ' else if(lua_isnil(L, i)) l = snprintf(buf, n, "%s ", "nil");')
132 | table.insert(txt, ' else if(lua_isboolean(L, i)) l = snprintf(buf, n, "%s ", "boolean");')
133 | table.insert(txt, ' else if(lua_isnumber(L, i)) l = snprintf(buf, n, "%s ", "number");')
134 | table.insert(txt, ' else if(lua_isstring(L, i)) l = snprintf(buf, n, "%s ", "string");')
135 | table.insert(txt, ' else if(lua_istable(L, i)) l = snprintf(buf, n, "%s ", "table");')
136 | table.insert(txt, ' else if(lua_isuserdata(L, i)) l = snprintf(buf, n, "%s ", "userdata");')
137 | table.insert(txt, ' else l = snprintf(buf, n, "%s ", "???");')
138 | table.insert(txt, ' if (l >= n) return;')
139 | table.insert(txt, ' buf += l;')
140 | table.insert(txt, ' n -= l;')
141 | table.insert(txt, ' }')
142 | table.insert(txt, '}')
143 | table.insert(txt, '#endif')
144 |
145 | self.__chelpers_added = true
146 | end
147 | end
148 |
149 | function CInterface:register(name)
150 | local txt = self.txt
151 | table.insert(txt, string.format('static const struct luaL_Reg %s [] = {', name))
152 | for _,reg in ipairs(self.registry) do
153 | table.insert(txt, string.format('{"%s", %s},', reg.name, reg.wrapname))
154 | end
155 | table.insert(txt, '{NULL, NULL}')
156 | table.insert(txt, '};')
157 | table.insert(txt, '')
158 | self.registry = {}
159 | end
160 |
161 | function CInterface:clearhistory()
162 | self.txt = {}
163 | self.registry = {}
164 | self.defaultArguments = {}
165 | end
166 |
167 | function CInterface:tostring()
168 | return table.concat(self.txt, '\n')
169 | end
170 |
171 | function CInterface:tofile(filename)
172 | local f = io.open(filename, 'w')
173 | f:write(table.concat(self.txt, '\n'))
174 | f:close()
175 | end
176 |
177 | local function bit(p)
178 | return 2 ^ (p - 1) -- 1-based indexing
179 | end
180 |
181 | local function hasbit(x, p)
182 | return x % (p + p) >= p
183 | end
184 |
185 | local function beautify(txt)
186 | local indent = 0
187 | for i=1,#txt do
188 | if txt[i]:match('}') then
189 | indent = indent - 2
190 | end
191 | if indent > 0 then
192 | txt[i] = string.rep(' ', indent) .. txt[i]
193 | end
194 | if txt[i]:match('{') then
195 | indent = indent + 2
196 | end
197 | end
198 | end
199 |
200 | local function tableinsertcheck(tbl, stuff)
201 | if stuff and not stuff:match('^%s*$') then
202 | table.insert(tbl, stuff)
203 | end
204 | end
205 |
206 | function CInterface:__writeheaders(txt, args, argoffset)
207 | local argtypes = self.argtypes
208 | local helpargs = {}
209 | local cargs = {}
210 | local argcreturned
211 | argoffset = argoffset or 0
212 |
213 | for i,arg in ipairs(args) do
214 | arg.i = i+argoffset
215 | arg.args = args -- in case we want to do stuff depending on other args
216 | assert(argtypes[arg.name], 'unknown type ' .. arg.name)
217 | setmetatable(arg, {__index=argtypes[arg.name]})
218 | arg.__metatable = argtypes[arg.name]
219 | tableinsertcheck(txt, arg:declare())
220 | local helpname = arg:helpname()
221 | if arg.returned then
222 | helpname = string.format('*%s*', helpname)
223 | end
224 | if arg.invisible and arg.default == nil then
225 | error('Invisible arguments must have a default! How could I guess how to initialize it?')
226 | end
227 | if arg.default ~= nil then
228 | if not arg.invisible then
229 | table.insert(helpargs, string.format('[%s]', helpname))
230 | end
231 | elseif not arg.creturned then
232 | table.insert(helpargs, helpname)
233 | end
234 | if arg.creturned then
235 | if argcreturned then
236 | error('A C function can only return one argument!')
237 | end
238 | if arg.default ~= nil then
239 | error('Obviously, an "argument" returned by a C function cannot have a default value')
240 | end
241 | if arg.returned then
242 | error('Options "returned" and "creturned" are incompatible')
243 | end
244 | argcreturned = arg
245 | else
246 | table.insert(cargs, arg:carg())
247 | end
248 | end
249 |
250 | return helpargs, cargs, argcreturned
251 | end
252 |
253 | function CInterface:__writechecks(txt, args, argset)
254 | local argtypes = self.argtypes
255 |
256 | local multiargset = argset
257 | argset = argset or 1
258 |
259 | local nopt = 0
260 | for i,arg in ipairs(args) do
261 | if arg.default ~= nil and not arg.invisible then
262 | nopt = nopt + 1
263 | end
264 | end
265 |
266 | for variant=0,(2^nopt)-1 do
267 | local opt = 0
268 | local currentargs = {}
269 | local optargs = {}
270 | local hasvararg = false
271 | for i,arg in ipairs(args) do
272 | if arg.invisible then
273 | table.insert(optargs, arg)
274 | elseif arg.default ~= nil then
275 | opt = opt + 1
276 | if hasbit(variant, bit(opt)) then
277 | table.insert(currentargs, arg)
278 | else
279 | table.insert(optargs, arg)
280 | end
281 | elseif not arg.creturned then
282 | table.insert(currentargs, arg)
283 | end
284 | end
285 |
286 | for _,arg in ipairs(args) do
287 | if arg.vararg then
288 | if hasvararg then
289 | error('Only one argument can be a "vararg"!')
290 | end
291 | hasvararg = true
292 | end
293 | end
294 |
295 | if hasvararg and not currentargs[#currentargs].vararg then
296 | error('Only the last argument can be a "vararg"')
297 | end
298 |
299 | local compop
300 | if hasvararg then
301 | compop = '>='
302 | else
303 | compop = '=='
304 | end
305 |
306 | if variant == 0 and argset == 1 then
307 | table.insert(txt, string.format('if(narg %s %d', compop, #currentargs))
308 | else
309 | table.insert(txt, string.format('else if(narg %s %d', compop, #currentargs))
310 | end
311 |
312 | for stackidx, arg in ipairs(currentargs) do
313 | table.insert(txt, string.format("&& %s", arg:check(stackidx)))
314 | end
315 | table.insert(txt, ')')
316 | table.insert(txt, '{')
317 |
318 | if multiargset then
319 | table.insert(txt, string.format('argset = %d;', argset))
320 | end
321 |
322 | for stackidx, arg in ipairs(currentargs) do
323 | tableinsertcheck(txt, arg:read(stackidx))
324 | end
325 |
326 | for _,arg in ipairs(optargs) do
327 | tableinsertcheck(txt, arg:init())
328 | end
329 |
330 | table.insert(txt, '}')
331 |
332 | end
333 | end
334 |
335 | function CInterface:__writecall(txt, args, cfuncname, cargs, argcreturned)
336 | local argtypes = self.argtypes
337 |
338 | for i = 1, #self.defaultArguments do
339 | table.insert(cargs, i, string.format('default_arg%d', i))
340 | end
341 |
342 | for _,arg in ipairs(args) do
343 | tableinsertcheck(txt, arg:precall())
344 | end
345 |
346 | if argcreturned then
347 | table.insert(txt, string.format('%s = %s(%s);', argtypes[argcreturned.name].creturn(argcreturned), cfuncname, table.concat(cargs, ',')))
348 | else
349 | table.insert(txt, string.format('%s(%s);', cfuncname, table.concat(cargs, ',')))
350 | end
351 |
352 | for _,arg in ipairs(args) do
353 | tableinsertcheck(txt, arg:postcall())
354 | end
355 |
356 | local nret = 0
357 | if argcreturned then
358 | nret = nret + 1
359 | end
360 | for _,arg in ipairs(args) do
361 | if arg.returned then
362 | nret = nret + 1
363 | end
364 | end
365 | table.insert(txt, string.format('return %d;', nret))
366 | end
367 |
368 | return CInterface
369 |
--------------------------------------------------------------------------------
/doc/argumenttypes.md:
--------------------------------------------------------------------------------
1 |
2 | ## Argument Types ##
3 |
4 | Any `CInterface` is initialized with a default `argtypes` list, at
5 | creation. This list tells to `CInterface` how to handle type names given
6 | to the [wrap()](higherlevelinterface.md#CInterface.wrap) method. The user can add more types to
7 | this list, if wanted (see [the next section](usertypes.md#CInterface.userargtypes)).
8 |
9 | ### Standard C types ###
10 | Standard type names include `unsigned char`, `char`, `short`,
11 | `int`, `long`, `float` and `double`. They define the corresponding
12 | C types, which are converted to/from
13 | [lua_Number](http://www.lua.org/manual/5.1/manual.html#lua_Number).
14 |
15 | Additionaly, `byte` is an equivalent naming for `unsigned char`, and
16 | `boolean` is interpreted as a boolean in Lua, and an int in C.
17 |
18 | `real` will also be converted to/from a `lua_Number`, while assuming that
19 | it is defined in C as `float` or `double`.
20 |
21 | Finally, `index` defines a long C value, which is going to be
22 | automatically incremented by 1 when going from C to Lua, and decremented by
23 | 1, when going from Lua to C. This matches Lua policy of having table
24 | indices starting at 1, and C array indices starting at 0.
25 |
26 | For all these number values, the `default` field (when defining the
27 | argument in [wrap()](higherlevelinterface.md##CInterface.wrap)) can take two types: either a
28 | number or a function (taking the argument table as argument, and returning a string).
29 |
30 | Note that in case of an `index` type, the given default value (or result
31 | given by the default initialization function) will be decremented by 1 when
32 | initializing the corresponging C `long` variable.
33 |
34 | Here is an example of defining arguments with a default value:
35 | ```lua
36 | {name="int", default=0}
37 | ```
38 | defines an optional argument which will of type `int` in C (lua_Number in Lua), and will take
39 | the value `0` if it is not present when calling the Lua function. A more complicated (but typical) example
40 | would be:
41 | ```lua
42 | {name="int", default=function(arg)
43 | return string.format("%s", arg.args[1]:carg())
44 | end}
45 | ```
46 | In this case, the argument will be set to the value of the first argument in the Lua function call, if not
47 | present at call time.
48 |
49 | ### Torch Tensor types ###
50 |
51 | `CInterface` also defines __Torch__ tensor types: `ByteTensor`,
52 | `CharTensor`, `ShortTensor`, `IntTensor`, `LongTensor`,
53 | `FloatTensor` and `DoubleTensor`, which corresponds to their
54 | `THByteTensor`, etc... counterparts. All of them assume that the
55 | [luaT](..:luaT) Tensor id (here for ByteTensor)
56 | ```
57 | const void *torch_ByteTensor_id;
58 | ```
59 | is defined beforehand, and properly initialized.
60 |
61 | Additionally, if you use C-templating style which is present in the TH library, you might want
62 | to use the `Tensor` typename, which assumes that `THTensor` is properly defined, as well as
63 | the macro `THTensor_()` and `torch_()` (see the TH library for more details).
64 |
65 | Another extra typename of interest is `IndexTensor`, which corresponds to a `THLongTensor` in C. Values in this
66 | LongTensor will be incremented/decremented when going from/to C/Lua to/from Lua/C.
67 |
68 | Tensor typenames `default` value in [wrap()](higherlevelinterface.md#CInterface.wrap) can take take two types:
69 | * A boolean. If `true`, the tensor will be initialized as empty, if not present at the Lua function call
70 | * A number (index). If not present at the Lua function call, the tensor will be initialized as _pointing_ to the argument at the given index (which must be a tensor of same type!).
71 | For e.g, the list of arguments:
72 | ```lua
73 | {
74 | {name=DoubleTensor, default=3},
75 | {name=double, default=1.0},
76 | {name=DoubleTensor}
77 | }
78 | ```
79 | The first two arguments are optional. The first one is a DoubleTensor which
80 | will point on the last (3rd) argument if not given. The second argument
81 | will be initialized to `1.0` if not provided.
82 |
83 | Tensor typenames can also take an additional field `dim` (a number) which will force a dimension
84 | check. E.g.,
85 | ```lua
86 | {name=DoubleTensor, dim=2}
87 | ```
88 | expect a matrix of doubles.
89 |
--------------------------------------------------------------------------------
/doc/example.md:
--------------------------------------------------------------------------------
1 | ## Example Use Case
2 |
3 | As an example is often better than lengthy explanations, let's consider the
4 | case of a function
5 |
6 | ```c
7 | int numel(THDoubleTensor *t);
8 | ```
9 |
10 | which returns the number of elements of `t`.
11 | Writing a complete wrapper of this function would look like:
12 |
13 | ```c
14 | static int wrapper_numel(lua_State *L)
15 | {
16 | THDoubleTensor *t;
17 |
18 | /* always good to check the number of arguments */
19 | if(lua_gettop(L) != 1)
20 | error("invalid number of arguments: expected");
21 |
22 | /* check if we have a tensor on the stack */
23 | /* we use the luaT library, which deals with Torch objects */
24 | /* we assume the torch_DoubleTensor_id has been already initialized */
25 | t = luaT_checkudata(L, 1, torch_DoubleTensor_id);
26 |
27 | /* push result on stack */
28 | lua_pushnumber(L, numel(t));
29 |
30 | /* the number of returned variables */
31 | return 1;
32 | }
33 | ```
34 |
35 | For anybody familiar with the Lua C API, this should look very simple (and
36 | _it is simple_, Lua has been designed for that!). Nevertheless, the
37 | wrapper contains about 7 lines of C code, for a quite simple
38 | function. Writing wrappers for C functions with multiple arguments, where
39 | some of them might be optional, can become very quickly a tedious task. The
40 | __wrap__ package is here to help the process. Remember however that even
41 | though you might be able to treat most complex cases with __wrap__,
42 | sometimes it is also good to do everything by hand yourself!
43 |
--------------------------------------------------------------------------------
/doc/highlevelinterface.md:
--------------------------------------------------------------------------------
1 | ## High Level Interface ##
2 |
3 | __wrap__ provides only one class: `CInterface`. Considering our easy example, a typical usage
4 | would be:
5 | ```lua
6 | require 'wrap'
7 |
8 | interface = wrap.CInterface.new()
9 |
10 | interface:wrap(
11 | "numel", -- the Lua name
12 | "numel", -- the C function name, here the same
13 | -- now we describe the 'arguments' of the C function
14 | -- (or possible returned values)
15 | {
16 | {name="DoubleTensor"},
17 | {name="int", creturned=true} -- this one is returned by the C function
18 | }
19 | )
20 |
21 | print(interface:tostring())
22 | ```
23 | `CInterface` contains only few methods. [wrap()](highlevelinterface.md#CInterface.wrap) is
24 | the most important one. [tostring()](highlevelinterface.md#CInterface.tostring) returns a
25 | string containing all the code produced until now. The wrapper generated
26 | by __wrap__ is quite similar to what one would write by hand:
27 | ```c
28 | static int wrapper_numel(lua_State *L)
29 | {
30 | int narg = lua_gettop(L);
31 | THDoubleTensor *arg1 = NULL;
32 | int arg2 = 0;
33 | if(narg == 1
34 | && (arg1 = luaT_toudata(L, 1, torch_DoubleTensor_id))
35 | )
36 | {
37 | }
38 | else
39 | luaL_error(L, "expected arguments: DoubleTensor");
40 | arg2 = numel(arg1);
41 | lua_pushnumber(L, (lua_Number)arg2);
42 | return 1;
43 | }
44 | ```
45 |
46 | We know describe the methods provided by `CInterface`.
47 |
48 |
49 | ### new() ###
50 |
51 | Returns a new `CInterface`.
52 |
53 |
54 | ### wrap(luaname, cfunction, arguments, ...) ###
55 |
56 | Tells the `CInterface` to generate a wrapper around the C function
57 | `cfunction`. The function will be called from Lua under the name
58 | `luaname`. The Lua _list_ `arguments` must also be provided. It
59 | describes _all_ the arguments of the C function `cfunction`.
60 | Optionally, if the C function returns a value and one would like to return
61 | it in Lua, this additional value can be also described in the argument
62 | list.
63 | ```lua
64 | {
65 | {name="DoubleTensor"},
66 | {name="int", creturned=true} -- this one is returned by the C function
67 | }
68 | ```
69 |
70 | Each argument is described also as a list. The list must at least contain
71 | the field `name`, which tells to `CInterface` what type of argument you
72 | want to define. In the above example,
73 | ```lua
74 | {name="DoubleTensor"}
75 | ```
76 | indicates to `CInterface` that the first argument of `numel()` is of type `DoubleTensor`.
77 |
78 | Arguments are defined into a table `CInterface.argtypes`, defined at the
79 | creation of the interface. Given a `typename`, the corresponding field
80 | in `interface.argtypes[typename]` must exist, such that `CInterface`
81 | knows how to handle the specified argument. A lot of types are already
82 | created by default, but the user can define more if needed, by filling
83 | properly the `argtypes` table. See the section [[argumenttypes.md#CInterface.argtypes]]
84 | for more details about defined types, and
85 | [how to define additional ones](usertypes.md#CInterface.userargtypes).
86 |
87 | #### Argument fields ####
88 |
89 | Apart the field `name`, each list describing an argument can contain several optional fields:
90 |
91 | `default`: this means the argument will optional in Lua, and the argument will be initialized
92 | with the given default value if not present in the Lua function call. The `default` value might
93 | have different meanings, depending on the argument type (see [[argumenttypes.md#CInterface.argtypes]] for more details).
94 |
95 | `invisible`: the argument will invisible _from Lua_. This special option requires `default` to be set,
96 | such that `CInterface` knows by what initialize this invisible argument.
97 |
98 | `returned`: if set to `true`, the argument will be returned by the Lua function. Note that several
99 | values might be returned at the same time in Lua.
100 |
101 | `creturned`: if `true`, tells to `CInterface` that this 'argument' is
102 | in fact the value returned by the C function. This 'argument' cannot have
103 | a `default` value. Also, as in C one can return only one value, only one
104 | 'argument' can contain this field! Mixing arguments which are `returned`
105 | and arguments which are `creturned` with `CInterface` is not
106 | recommended: use with care.
107 |
108 | While these optional fields are generic to any argument types, some types might define additional optional fields.
109 | Again, see [[argumenttypes.md#CInterface.argtypes]] for more details.
110 |
111 | #### Handling multiple variants of arguments ####
112 |
113 | Sometimes, one cannot describe fully the behavior one wants with only a set of possible arguments.
114 | Take the example of the `cos()` function: we might want to apply it to a number, if the given argument
115 | is a number, or to a Tensor, if the given argument is a Tensor.
116 |
117 | `wrap()` can be called with extra pairs of `cname, args` if needed. (There are no limitations on the number extra paris).
118 | For example, if you need to handle three cases, it might be
119 | ```lua
120 | interface:wrap(luaname, cname1, args1, cname2, args2, cname3, args3)
121 | ```
122 | For each given C function name `cname`, the corresponding argument list `args` should match.
123 | As a more concrete example, here is a way to generate a wrapper for `cos()`, which would handle both numbers
124 | and DoubleTensors.
125 | ```lua
126 | interface:wrap("cos", -- the Lua function name
127 |
128 | "THDoubleTensor_cos", { -- C function called for DoubleTensor
129 | {name="DoubleTensor", default=true, returned=true}, -- returned tensor (if not present, we create an empty tensor)
130 | {name="DoubleTensor"} -- input tensor
131 | },
132 |
133 | "cos", { -- the standard C math cos function
134 | {name="double", creturned="true"}, -- returned value
135 | {name="double"} -- input value
136 | }
137 | )
138 | ```
139 |
140 |
141 | ### print(str) ###
142 |
143 | Add some hand-crafted code to the existing generated code. You might want to do that if your wrapper
144 | requires manual tweaks. For e.g., in the example above, the "id" related to `torch.DoubleTensor`
145 | needs to be defined beforehand:
146 | ```lua
147 | interface:print([[
148 | const void* torch_DoubleTensor_id;
149 | ]])
150 | ```
151 |
152 |
153 | ### luaname2wrapname(name) ###
154 |
155 | This method defines the name of each generated wrapping function (like
156 | `wrapper_numel` in the example above), given the Lua name of a function
157 | (say `numel`). In general, this has little importance, as the wrapper is
158 | a static function which is not going to be called outside the scope of the
159 | wrap file. However, if you generate some complex wrappers, you might want
160 | to have a control on this to avoid name clashes. The default is
161 | ```lua
162 | function CInterface:luaname2wrapname(name)
163 | return string.format("wrapper_%s", name)
164 | end
165 | ```
166 | Changing it to something else can be easily done with (still following the example above)
167 | ```lua
168 | function interface:luaname2wrapname(name)
169 | return string.format("my_own_naming_%s", name)
170 | end
171 | ```
172 |
173 | ### register(name) ###
174 |
175 | Produces C code defining a
176 | [luaL_Reg](http://www.lua.org/manual/5.1/manual.html#luaL_Reg) structure
177 | (which will have the given `name`). In the above example, calling
178 | ```lua
179 | interface:register('myfuncs')
180 | ```
181 | will generate the following additional code:
182 | ```c
183 | static const struct luaL_Reg myfuncs [] = {
184 | {"numel", wrapper_numel},
185 | {NULL, NULL}
186 | };
187 | ```
188 |
189 | This structure is meant to be passed as argument to
190 | [luaL_register](http://www.lua.org/manual/5.1/manual.html#luaL_register),
191 | such that Lua will be aware of your new functions. For e.g., the following
192 | would declare `mylib.numel` in Lua:
193 | ```lua
194 | interface:print([[
195 | luaL_register(L, "mylib", myfuncs);
196 | ]])
197 | ```
198 |
199 |
200 | ### tostring() ###
201 |
202 | Returns a string containing all the code generated by the `CInterface`
203 | until now. Note that the history is not erased.
204 |
205 |
206 | ### tofile(filename) ###
207 |
208 | Write in the file (named after `filename`) all the code generated by the
209 | `CInterface` until now. Note that the history is not erased.
210 |
211 |
212 | ### clearhistory() ###
213 |
214 | Forget about all the code generated by the `CInterface` until now.
215 |
216 |
--------------------------------------------------------------------------------
/doc/index.md:
--------------------------------------------------------------------------------
1 | # CWrap package #
2 |
3 | The __cwrap__ package helps you to automate the generation of Lua/C wrappers
4 | around existing C functions, such that these functions would be callable
5 | from Lua. This package is used by the __torch__ package, but does not depend on
6 | anything, and could be used by anyone using Lua.
7 | The documentation is organized as follows :
8 |
9 | * [Example Use Case](example.md)
10 | * [High Level Interface](highlevelinterface.md)
11 | * [Argument Types](argumenttypes.md)
12 | * [User Types](usertypes.md)
13 |
14 | __DISCLAIMER__ Before going any further, we assume the reader has a good
15 | knowledge of how to interface C functions with Lua. A good start would be
16 | the [Lua reference manual](http://www.lua.org/manual/5.1), or the book
17 | [Programming in Lua](http://www.inf.puc-rio.br/~roberto/pil2).
18 |
19 |
20 |
--------------------------------------------------------------------------------
/doc/usertypes.md:
--------------------------------------------------------------------------------
1 |
2 | ## User Types ##
3 |
4 | Types available by default in `CInterface` might not be enough for your needs. Also, sometimes you might
5 | need to change sliglty the behavior of existing types. In that sort of cases, you will need to
6 | know more about what is going on under the hood.
7 |
8 | When you do a call to [wrap()](highlevelinterface.md#CInterface.wrap),
9 | ```lua
10 | interface:wrap(
11 | "numel", -- the Lua name
12 | "numel", -- the C function name, here the same
13 | -- now we describe the 'arguments' of the C function
14 | -- (or possible returned values)
15 | {
16 | {name="DoubleTensor"},
17 | {name="int", creturned=true} -- this one is returned by the C function
18 | }
19 | )
20 | ```
21 | the method will examine each argument you provide. For example, let's consider:
22 | ```lua
23 | {name="int", creturned=true}
24 | ```
25 | Considering the argument field `name`, __wrap__ will check if the field
26 | `interface.argtypes['int']` exists or not. If it does not exist, an error will be raised.
27 |
28 | In order to describe what happens next, we will now denote
29 | ```lua
30 | arg = {name="int", creturned=true}
31 | ```
32 | First thing which is done is assigning `interface.argtypes['int']` as a metatable to `arg`:
33 | ```lua
34 | setmetatable(arg, interface.argtypes[arg.name])
35 | ```
36 | Then, a number of fields are populated in `arg` by __wrap__:
37 | ```lua
38 | arg.i = 2 -- argument index (in the argument list) in the wrap() call
39 | arg.__metatable = interface.argtypes[arg.name]
40 | arg.args = ... -- the full list of arguments given in the wrap() call
41 | ```
42 |
43 | [wrap()](highlevelinterface.md#CInterface.wrap) will then call a several methods which are
44 | assumed to be present in `arg` (see below for the list). Obviously, in
45 | most cases, methods will be found in the metatable of `arg`, that is in
46 | `interface.argtypes[arg.name]`. However, if you need to override a method
47 | behavior for one particular argument, this method could be defined in the
48 | table describing the argument, when calling [wrap()](highlevelinterface.md#CInterface.wrap).
49 |
50 | The extra fields mentionned above (populated by __wrap__) can be used in the argument
51 | methods to suit your needs (they are enough to handle most complex cases).
52 |
53 | We will now describe methods which must be defined for each type. We will
54 | take as example `boolean`, to make things more clear. If you want to see
55 | more complex examples, you can have a look into the `types.lua` file,
56 | provided by the __wrap__ package.
57 |
58 |
59 | ### helpname(arg) ###
60 |
61 | Returns a string describing (in a human readable fashion) the name of the given arg.
62 |
63 | Example:
64 | ```lua
65 | function helpname(arg)
66 | return "boolean"
67 | end
68 | ```
69 |
70 |
71 | ### declare(arg) ###
72 |
73 | Returns a C code string declaring the given arg.
74 |
75 | Example:
76 | ```lua
77 | function declare(arg)
78 | return string.format("int arg%d = 0;", arg.i)
79 | end
80 | ```
81 |
82 |
83 | ### check(arg, idx) ###
84 |
85 | Returns a C code string checking if the value at index `idx` on the Lua stack
86 | corresponds to the argument type. The string will appended in a `if()`, so it should
87 | not contain a final `;`.
88 |
89 | Example:
90 | ```lua
91 | function check(arg, idx)
92 | return string.format("lua_isboolean(L, %d)", idx)
93 | end
94 | ```
95 |
96 |
97 | ### read(arg, idx) ###
98 |
99 | Returns a C code string converting the value a index `idx` on the Lua stack, into
100 | the desired argument. This method will be called __only if__ the C check given by
101 | [check()](#CInterface.arg.check) succeeded.
102 |
103 | Example:
104 | ```lua
105 | function read(arg, idx)
106 | return string.format("arg%d = lua_toboolean(L, %d);", arg.i, idx)
107 | end
108 | ```
109 |
110 |
111 | ### init(arg) ###
112 |
113 | Returns a C code string initializing the argument by its default
114 | value. This method will be called __only if__ (1) `arg` has a `default`
115 | field and (2) the C check given by [check()](#CInterface.arg.check)
116 | failed (so the C code in [read()](#CInterface.arg.read) was not called).
117 |
118 | Example:
119 | ```lua
120 | function init(arg)
121 | local default
122 | if arg.default then
123 | default = 1
124 | else
125 | default = 0
126 | end
127 | return string.format("arg%d = %s;", arg.i, default)
128 | end
129 | ```
130 |
131 |
132 | ### carg(arg) ###
133 |
134 | Returns a C code string describing how to pass
135 | the given `arg` as argument when calling the C function.
136 |
137 | In general, it is just the C arg name itself (except if you need to pass
138 | the argument "by address", for example).
139 |
140 | Example:
141 | ```lua
142 | function carg(arg)
143 | return string.format('arg%d', arg.i)
144 | end
145 | ```
146 |
147 |
148 | ### creturn(arg) ###
149 |
150 | Returns a C code string describing how get the argument if it
151 | is returned from the C function.
152 |
153 | In general, it is just the C arg name itself (except if you need to assign
154 | a pointer value, for example).
155 |
156 | ```lua
157 | function creturn(arg)
158 | return string.format('arg%d', arg.i)
159 | end
160 | ```
161 |
162 |
163 | ### precall(arg) ###
164 |
165 | Returns a C code string if you need to execute specific code related to
166 | `arg`, before calling the C function.
167 |
168 | For e.g., if you created an object in the calls before, you might want to
169 | put it on the Lua stack here, such that it is garbage collected by Lua, in case
170 | the C function call fails.
171 |
172 | ```lua
173 | function precall(arg)
174 | -- nothing to do here, for boolean
175 | end
176 | ```
177 |
178 |
179 | ### postcall(arg) ###
180 |
181 | Returns a C code string if you need to execute specific code related to
182 | `arg`, after calling the C function. You can for e.g. push the argument
183 | on the stack, if needed.
184 |
185 | ```lua
186 | function postcall(arg)
187 | if arg.creturned or arg.returned then
188 | return string.format('lua_pushboolean(L, arg%d);', arg.i)
189 | end
190 | end
191 | ```
192 |
193 |
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | local cwrap = {}
2 |
3 | cwrap.types = require 'cwrap.types'
4 | cwrap.CInterface = require 'cwrap.cinterface'
5 | cwrap.CInterface.argtypes = cwrap.types
6 |
7 | return cwrap
8 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: cwrap
2 | theme : simplex
3 | repo_url : https://github.com/torch/cwrap
4 | use_directory_urls : false
5 | markdown_extensions: [extra]
6 | docs_dir : doc
7 | pages:
8 | - [index.md, CWrap]
9 | - [example.md, Example Use Case]
10 | - [highlevelinterface.md, High Level Interface]
11 | - [argumenttypes.md, Argument Types]
12 | - [usertypes.md, User Types]
13 |
--------------------------------------------------------------------------------
/rocks/cwrap-scm-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "cwrap"
2 | version = "scm-1"
3 |
4 | source = {
5 | url = "git://github.com/torch/cwrap.git",
6 | }
7 |
8 | description = {
9 | summary = "Advanced automatic wrapper for C functions",
10 | detailed = [[
11 | ]],
12 | homepage = "https://github.com/torch/cwrap",
13 | license = "BSD"
14 | }
15 |
16 | dependencies = {
17 | "lua >= 5.1",
18 | }
19 |
20 | build = {
21 | type = "builtin",
22 | modules = {
23 | ["cwrap.init"] = "init.lua",
24 | ["cwrap.cinterface"] = "cinterface.lua",
25 | ["cwrap.types"] = "types.lua",
26 | },
27 | install = {
28 | lua = {
29 | ["cwrap.README"] = "README.md"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/types.lua:
--------------------------------------------------------------------------------
1 | local argtypes = {}
2 |
3 | local function interpretdefaultvalue(arg)
4 | local default = arg.default
5 | if type(default) == 'boolean' then
6 | if default then
7 | return '1'
8 | else
9 | return '0'
10 | end
11 | elseif type(default) == 'number' then
12 | return tostring(default)
13 | elseif type(default) == 'string' then
14 | return default
15 | elseif type(default) == 'function' then
16 | default = default(arg)
17 | assert(type(default) == 'string', 'a default function must return a string')
18 | return default
19 | elseif type(default) == 'nil' then
20 | return nil
21 | else
22 | error('unknown default type value')
23 | end
24 | end
25 |
26 | argtypes.index = {
27 |
28 | helpname = function(arg)
29 | return "index"
30 | end,
31 |
32 | declare = function(arg)
33 | -- if it is a number we initialize here
34 | local default = tonumber(interpretdefaultvalue(arg)) or 1
35 | return string.format("long arg%d = %d;", arg.i, tonumber(default)-1)
36 | end,
37 |
38 | check = function(arg, idx)
39 | return string.format("lua_isnumber(L, %d)", idx)
40 | end,
41 |
42 | read = function(arg, idx)
43 | return string.format("arg%d = (long)lua_tonumber(L, %d)-1;", arg.i, idx)
44 | end,
45 |
46 | init = function(arg)
47 | -- otherwise do it here
48 | if arg.default then
49 | local default = interpretdefaultvalue(arg)
50 | if not tonumber(default) then
51 | return string.format("arg%d = %s-1;", arg.i, default)
52 | end
53 | end
54 | end,
55 |
56 | carg = function(arg)
57 | return string.format('arg%d', arg.i)
58 | end,
59 |
60 | creturn = function(arg)
61 | return string.format('arg%d', arg.i)
62 | end,
63 |
64 | precall = function(arg)
65 | if arg.returned then
66 | return string.format('lua_pushnumber(L, (lua_Number)arg%d+1);', arg.i)
67 | end
68 | end,
69 |
70 | postcall = function(arg)
71 | if arg.creturned then
72 | return string.format('lua_pushnumber(L, (lua_Number)arg%d+1);', arg.i)
73 | end
74 | end
75 | }
76 |
77 | for _,typename in ipairs({"real", "unsigned char", "char", "short", "int", "long", "float", "double"}) do
78 | argtypes[typename] = {
79 |
80 | helpname = function(arg)
81 | return typename
82 | end,
83 |
84 | declare = function(arg)
85 | -- if it is a number we initialize here
86 | local default = tonumber(interpretdefaultvalue(arg)) or 0
87 | return string.format("%s arg%d = %g;", typename, arg.i, default)
88 | end,
89 |
90 | check = function(arg, idx)
91 | return string.format("lua_isnumber(L, %d)", idx)
92 | end,
93 |
94 | read = function(arg, idx)
95 | return string.format("arg%d = (%s)lua_tonumber(L, %d);", arg.i, typename, idx)
96 | end,
97 |
98 | init = function(arg)
99 | -- otherwise do it here
100 | if arg.default then
101 | local default = interpretdefaultvalue(arg)
102 | if not tonumber(default) then
103 | return string.format("arg%d = %s;", arg.i, default)
104 | end
105 | end
106 | end,
107 |
108 | carg = function(arg)
109 | return string.format('arg%d', arg.i)
110 | end,
111 |
112 | creturn = function(arg)
113 | return string.format('arg%d', arg.i)
114 | end,
115 |
116 | precall = function(arg)
117 | if arg.returned then
118 | return string.format('lua_pushnumber(L, (lua_Number)arg%d);', arg.i)
119 | end
120 | end,
121 |
122 | postcall = function(arg)
123 | if arg.creturned then
124 | return string.format('lua_pushnumber(L, (lua_Number)arg%d);', arg.i)
125 | end
126 | end
127 | }
128 | end
129 |
130 | argtypes.byte = argtypes['unsigned char']
131 |
132 | argtypes.boolean = {
133 |
134 | helpname = function(arg)
135 | return "boolean"
136 | end,
137 |
138 | declare = function(arg)
139 | -- if it is a number we initialize here
140 | local default = tonumber(interpretdefaultvalue(arg)) or 0
141 | return string.format("int arg%d = %d;", arg.i, tonumber(default))
142 | end,
143 |
144 | check = function(arg, idx)
145 | return string.format("lua_isboolean(L, %d)", idx)
146 | end,
147 |
148 | read = function(arg, idx)
149 | return string.format("arg%d = lua_toboolean(L, %d);", arg.i, idx)
150 | end,
151 |
152 | init = function(arg)
153 | -- otherwise do it here
154 | if arg.default then
155 | local default = interpretdefaultvalue(arg)
156 | if not tonumber(default) then
157 | return string.format("arg%d = %s;", arg.i, default)
158 | end
159 | end
160 | end,
161 |
162 | carg = function(arg)
163 | return string.format('arg%d', arg.i)
164 | end,
165 |
166 | creturn = function(arg)
167 | return string.format('arg%d', arg.i)
168 | end,
169 |
170 | precall = function(arg)
171 | if arg.returned then
172 | return string.format('lua_pushboolean(L, arg%d);', arg.i)
173 | end
174 | end,
175 |
176 | postcall = function(arg)
177 | if arg.creturned then
178 | return string.format('lua_pushboolean(L, arg%d);', arg.i)
179 | end
180 | end
181 | }
182 |
183 | return argtypes
184 |
--------------------------------------------------------------------------------