├── 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 | --------------------------------------------------------------------------------