Mimicking C APIs in Lua

Last Sunday, the latest announcement of luaposix [1] lead me to state the following observation on the Lua mailing list [2]:

From: Sean Conner <sean@conman.org>
To: Lua mailing list <lua-l@lists.lua.org>
Subject: Literal mimicking of C API (Application Programming Interface)s in Lua (split from Re: [ANN] luaposix 33.3.0 released)
Date: Sat, 28 Feb 2015 23:16:51 -0500
> This isn't about luaposix per se, but this is prompting this observation about Lua wrappers for C APIs: they tend to mimic it quite literally (to the point where I think I've said this before: if I wanted to code in C, I know where to find it).

I checked, and sure enough, the Lua [3] wrapper around syslog() was what I expected (and had found in several other syslog() wrappers for Lua)—a very thin wrapper [4] over syslog() leading to this in Lua:

>
```
syslog.syslog(syslog.LOG_WARNING,string.format("foobar %d is at %d capacity",fooid,foocap))
```

I seem to be the only one who make it easy to use syslog() in Lua:

>
```
syslog('warning',"foobar %d is at %d capcaity",fooid,foocap)
```

I went to the trouble of ma king the module itself callable [5] (but you can still call syslog.log() if you want) and handle the call to string.format() be half of the caller [6] because in 90% of the cases I call syslog(), I need formatted output.

It just struck me as odd that not many writers of C-based Lua modules bother to make it easy to use their modules and I was about the thought process behind it (or the lack of thought process). The best answer came from Gary Vaughan, author of luaposix:

From: "Gary V. Vaughan" <XXXXXXXXXXXXXXX>
To: Lua mailing list <lua-l@lists.lua.org>
Subject: Re: Literal mimicking of C APIs in Lua (split from Re: [ANN] luaposix 33.3.0 released)
Date: Sun, 1 Mar 2015 09:02:00 +0000
> It's precisely because I don't want to code in C either that the low- level API of luaposix aspires to be as thin a wrapper for the C API as possible. It's easier, faster and less error-prone to write the Luaish API on top of the thin C wrappers in Lua than it is to write the fancy stuff in C.
Not only that, this leaves the door open to replace the C bindings with an FFI (Foreign Function Interface) binding that the Luaish layer can equally sit on top of and ultimately shipping no C code at all in luaposix… as long as a dependency on LuaJIT and/or LuaFFI is an acceptable compromise. When the low-level C code implements the user- facing API, all of this is a lot more difficult.

He's got a good point—make the C layer as thin as possible:

>
```
static int syslog_syslog(lua_State *L)
{
syslog(luaL_checkinteger(L,1),"%s",luaL_checkstring(L,2));
return 0;
}
```

and leave the fancy stuff up to Lua:

>
```
local m_priority =
{
emerg = 0, emergency = 0,
alert = 1,
crit = 2, critical = 2,
err = 3, error = 3,
warn = 4, warning = 4,
notice = 5,
info = 6, information = 6,
debug = 7
}
function syslog(priority,...)
syslogcore.syslog(m_priority[priority],string.format(...))
end
```

But there is a flaw when he metions LuaJIT [7] (and LuaFFI in general):

C declarations are not passed through a C pre-processor, yet. No pre- processor tokens are allowed, except for #pragma pack. Replace #define in existing C header files with enum, static const or typedef and/or pass the files through an external C pre-processor (once). Be careful not to include unneeded or redundant declarations from unrelated header files.

“ffi.* API Functions [8]”

In the case of syslog(), it's not that big an issue—the levels are standardized [9] so it's easy to supply the proper values, but it's different for a function like socket(). A thin C wrapper is trivial:

>
```
static int socket_socket(lua_State *L)
{
lua_pushinteger(
L,
socket(
luaL_checkinteger(L,1),
luaL_checkinteger(L,2),
luaL_checkinteger(L,3)
)
);
lua_pushinteger(L,errno);
return 2;
}
```

Not so easy is defining the values for those three parameters. The first is an arbitrary number defining the “family” the socket belongs to, ether IP (Internet Protocol), IPv6 (Internet Protocol Version 6), Unix domain, etc. The second parameter further defines the socket type, whether it's a stream based socket, or you want actual packets. The last parameter can, in 99% of the cases, be 0, so we shall ignore it. In C, it's typically called like:

>
```
s = socket(AF_INET,SOCK_STREAM,0);
```

But the actual value of AF_INET may vary from operating system to operating system (they do—I checked a few systems to make sure) so it's not always as straightforward to skip C entirely when wrapping a C API with LuaJIT.

Overall though, the idea is sound and I do find it intriguing, but not enough to stop the approach I've been taking.

[1] http://github.com/luaposix/luaposix/

[2] http://lua-/

[3] http://www.lua.org/

[4] https://github.com/luaposix/luaposix/blob/1cb93ff3646382ae79c6205029ee5

[5] https://github.com/spc476/lua-

[6] https://github.com/spc476/lua-

[7] http://luajig.org/

[8] http://luajit.org/ext_ffi_api.html

[9] https://www.ietf.org/rfc/rfc3164.txt

Gemini Mention this post

Contact the author