Chapa 1)


Two ways of creating ".so"s for Lua, one very fast, both using Emacs and eev (2024)

This page is about this video: YT, subtitles,
and about this intro: (find-lua-tutorial-intro).
   Annoucements: eev, lua-l, Emacs News.


1. Introduction

It is relatively easy to define Lua functions in a .c file, compile the .c to a .so, and then load the .so with package.loadlib or require. If you are just learning how to do that you will prefer to start with simple examples, like the one below.

This C file - /tmp/clua001.c - defines a Lua function called foo that just returns the string "FOO":

#include "lauxlib.h"
static int my_foo(lua_State* L) {
  lua_pushstring(L, "FOO");
  return 1;
static const struct luaL_reg clua001_lib[] = {
  {"foo", my_foo},
LUALIB_API int luaopen_clua001(lua_State *L) {
  lua_pushvalue(L, LUA_GLOBALSINDEX);
  luaL_openlib(L, NULL, clua001_lib, 0);
  return 0;

We can compile it with:

CFLAGS="-g -Wall -shared"
gcc $CFLAGS -I$LUADIR -o /tmp/clua001.so /tmp/clua001.c

and test it from Lua with:

package.loadlib("/tmp/clua001.so", "luaopen_clua001")()
print(foo())   --> FOO

Does everybody write these things by hand?
What are the existing tools for generating these blocks from templates?
I don't know! I don't know!

2. Why?

I need to learn TikZ and (more) Lpeg. I find them very hard, so I created tools to help me learn them, and documented those tools. Here is one of my pages about TikZ, and here is one of my pages about Lpeg.

In jan/2024 I saw that I would need to extend Lpeg with some functions that I would have to write in C - I sent this e-mail to the mailing list, and Roberto answered this. I found that writing Lua functions in C was hard for me, and I saw that I could adapt the template-based tools that I was using for TikZ and Lpeg to make them help me generate ".so"s. So...

3. A 5-minute hack: `find-luaso-links'

The first tool that I wrote was `find-luaso-links', that is a "5-minute hack" in the sense of my presentation at the EmacsConf 2020. Its intended workflow should be obvious to anyone who has practice with eev, but these people are very few, so I also needed something - see the next section! - that would be easy for people who only know Lua and eepitch.

`find-luaso-links' is explained very briefly in this section of the "intro",

(find-lua-tutorial-intro "3. The C API")

with a cryptic exercise that says "delete all the angg-isms". The first screenshot below shows a case with the angg-isms present (the ".c" there is this file) and the second shows a case with all the angg-isms deleted, in which the ".c" is /tmp/dummy2.c:

In both cases I edited the test blocks a bit to make them use find-3ee; I used M-# to generate the find-3EE/find-3ee pair.

`find-luaso-links' is explained in the video starting at 02:19.

4. The very fast way: CLua1.lua and buildandload

The "very fast way" uses a Lua function called buildandload to create the ".c", generate the ".so", and load the ".so", and it uses files whose names are like these


to let us load several ".so"s in the same Lua session without problems. You can try the "very fast way" in two ways: either by running the eepitch blocks in these sections of the intro,

(find-lua-tutorial-intro "4. CLua1.lua")
(find-lua-tutorial-intro "5. CLua1.lua from the outside")

or by copying-and-pasting the big eepitch block below to an Emacs buffer and typing f8 on each line. Its red bullets will probably lose their colors; this is explained here.

 Download it into /tmp/CLua1/:
rm -Rfv /tmp/CLua1/
mkdir   /tmp/CLua1/
cd      /tmp/CLua1/
wget http://anggtwu.net/LUA/lua50init.lua
wget http://anggtwu.net/LUA/Dang1.lua
wget http://anggtwu.net/LUA/CLua1.lua

 Make `find-clua', LUA_INIT and LUA_PATH point to /tmp/CLua1/:
 (code-c-d "clua"    "/tmp/CLua1/" :anchor)
 (setenv "LUA_INIT" "@/tmp/CLua1/lua50init.lua")
 (setenv "LUA_PATH"  "/tmp/CLua1/?.lua;;")

require "CLua1"   -- (find-clua "CLua1.lua")

-- Choose one:
CLua.__index.compile = CLua.__index.mac
CLua.__index.compile = CLua.__index.debian

buildandload('minusdiv', [=[
  lua_pushnumber(L, lua_tonumber(L, 1) - lua_tonumber(L, 2));
  lua_pushnumber(L, lua_tonumber(L, 1) / lua_tonumber(L, 2));
  return 2;
print(minusdiv(20, 2))                  --> 18 10
print(minusdiv(20, 2, 42, 99, 300, 3))  --> 18 10

buildandload('minusdiv', [=[
  lua_pushnumber(L, lua_tonumber(L, -2) - lua_tonumber(L, -1));
  lua_pushnumber(L, lua_tonumber(L, -3) / lua_tonumber(L, -2));
  return 2;
print(minusdiv(20, 2))                  --> 18 10
print(minusdiv(20, 2, 42, 99, 300, 3))  --> 297 100


You should see something like this: