Calling an existing C function from Lua
This is a follow-on question from this thread.
Apparently calling C (any external FFI function really) from Lua isn't as simple as it is when using LuaJIT's FFI.
However, I haven't been able to find any definitive way of doing it. lua.org articles are rather waffly and are more concerned with writing C that can interact with Lua.
I'm not interested in that right now; I just want to know how to call functions in existing external libraries.
For example, the C runtime exports a function puts, which takes a pointer to a zero-terminated string, and returns a 32-bit count of the number of characters output.
Its signature in C syntax is int puts(char*). How would I define that in Lua, and how would I call it?
I'm mainly curious as I've heard that Lua is supposedly good at C FFI, and I want to see how good. Unless people were again talking about LuaJIT!
5
u/revereddesecration 1d ago
What’s your goal? Or is this just curiosity?
1
u/sal1303 1d ago
As I mentioned in my other thread, I maintain a scripting language of my own.
One feature is a built-in FFI, which I considered unusual among scripting languages, where calling external functions tends to be a clunky process. But I'd heard that Lua was designed for easy interop with C and started investigating.
The first FFI I saw was LuaJIT's, which looked decent and with similar abilities to mine. But it turned out that regular Lua doesn't have a built-in FFI of that standard, and requires extensions to simplify the process.
1
u/immortalx74 1d ago
The way I understand it, is that the claim of easy integration with C of vanilla Lua, is to write programs/libs tailored to it and not the other way around. For existing C code I suppose you could write a C wrapper, but since LuaJIT + FFI exists, that's the easiest path.
1
u/Difficult-Value-3145 1d ago
I have never gotten luajit ffi or lua++ or any of that to work I found it easier to just write the function takes lua state as a variable returns the number of results you want to pass back. Now I learn kinda weird some times and I miss random stuff I really should get but never got any working results from any third party designed to turn a function from a header file of a club into callable lua. I found writing the wrappers fairly easy with lua c api I keep saying to myself I'm going to try doing it for python or something for comparison at least haven't thou. Bash dosnt count there is a reason bash is so non intuitive and part of that is so you can call programs from other languages seemingly natively if they are written for it without any special bash c api
1
u/immortalx74 1d ago
What I've done successfully numerous times was call Windows API or other external libraries code from Lua via LuaJIT's FFI. You just write the function signatures in a file and pass it to the ffi.cdef function. It's very straightforward.
1
u/Kritzel-Kratzel 1d ago
OneLuaPro provides https://github.com/OneLuaPro/lua-ffi for instant DLL access. However, Windows only at the moment.
1
u/xoner2 18h ago
Like I said in your previous thread, luaffi library works for PUC Lua 5.1 and 5.2. It uses the dynasm from LuaJIT. There's no reason it can't be compiled for 5.3 to 5.5.
Here's an example, I use this from both 5.1 and LuaJIT:
local cdefs = [[
typedef unsigned long DWORD;
typedef long LONG;
typedef long long LONGLONG;
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
} DUMMYSTRUCTNAME;
struct {
DWORD LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;
typedef int BOOL;
BOOL QueryPerformanceCounter(
LARGE_INTEGER *lpPerformanceCount
);
BOOL QueryPerformanceFrequency(
LARGE_INTEGER *lpFrequency
);
]]
local ffi = require 'ffi'
ffi.cdef (cdefs)
local freq = ffi.new ('LARGE_INTEGER[1]')
local ticks = ffi.new ('LARGE_INTEGER[1]')
local function GetSeconds ()
ffi.C.QueryPerformanceFrequency(freq)
ffi.C.QueryPerformanceCounter(ticks)
return tonumber (ticks[0].QuadPart) / tonumber (freq[0].QuadPart)
end
return GetSeconds
1
u/sal1303 12h ago
I looked at that link. I'm on Windows, and it says:
On windows use msvcbuild.bat in a visual studio cmd prompt
VS, and MS tools in general are huge, so I'm not going to do that. (I haven't managed to get it to work for years anyway). I guess there is no simple way of making this available?
(I develop my own small-scale language products which are the antithesis of tools like VS/MSVC, gcc, clang and LLVM. Or, for scripting languages, compared with ones like CPython.
Lua is different in being on a similar scale to my stuff, and its C source code, especially in its one-file version, is a test input to my C compiler project.)
1
u/xoner2 10h ago
(I haven't managed to get it to work for years anyway). I guess there is no simple way of making this available?
MSVC works now.
Which C runtime are you on?
Are you able to compile Lua with your C compiler project? If yes, try compiling luaffi too. It requires very few functions: memmove, memset, memchr, memcpy, strchr. The Lua dll/exe must be compiled with
dlopensupport.
1
u/smog_alado 10h ago
The Lua–C API looks a bit unusual at first, because of the Lua Stack, but it's actually a clever design. Compared to other programming languages:
- The API surface is quite small
- The API is comprehensive, covering everything that Lua can do.
- The API is safe: it is very hard to leak or corrupt memory.
To call an external C function you need to create a wrapper function that translate between the Lua world and the C world. You can find examples of this in Lua's own standard library. At the end of these files you should be able to find a luaL_Reg array with a list of functions in the library. Each of the functions listed in this array should be one of those wrapper functions I just mentioned, with a signature that receives lua_State * and returns int.
6
u/didntplaymysummercar 1d ago
This is covered in the manual.
You'd write a C function that takes lua_State and returns int and call puts inside it, taking argument from Lua (using lua_tostring, or luaL_checkstring , etc.) and returning something or nothing by pushing these values and return their amount.
All Lua built in libraries are implemented like that too so you can read those.
An int returning lua_State taking function is the only type you can register to Lua. This is usually not called "ffi" by most BTW.
The LuaJIT has own ffi and for stock Lua ffi libs exist too, and (especially in C++) people use wrappers that automatically wrap functions and structs for you too.