@@ -1317,15 +1317,6 @@ pub const Lua = opaque {
13171317 return c .lua_replace (asState (lua ), index );
13181318 }
13191319
1320- /// Sets the `CFunction` `f` as the new value of global `name`.
1321- ///
1322- /// From: `void lua_register(lua_State *L, const char *name, lua_CFunction f);`
1323- /// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_register
1324- /// Stack Behavior: `[-0, +0, e]`
1325- pub fn register (lua : * Lua , name : [:0 ]const u8 , f : CFunction ) void {
1326- return c .lua_register (asState (lua ), asCString (name ), asCFn (f ));
1327- }
1328-
13291320 /// Returns whether the two values in given acceptable indices are equal, following the semantics of the Lua `==`
13301321 /// operator, which may call metamethods.
13311322 ///
@@ -2201,6 +2192,67 @@ pub const Lua = opaque {
22012192 _ = c .luaL_argerror (asState (lua ), arg_n , ptr );
22022193 }
22032194 }
2195+
2196+ /// Represents named functions that belong to a library that can be registered by a call to the `registerLibrary()`
2197+ /// function.
2198+ ///
2199+ /// Any array of this type **MUST** be terminated by a sentinel value in which both `name` and `func` are set to
2200+ /// `null`. Refer to `RegEnd`.
2201+ ///
2202+ /// From: `typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg;`
2203+ /// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_Reg
2204+ pub const Reg = struct {
2205+ name : ? [* :0 ]const u8 ,
2206+ func : ? Lua.CFunction ,
2207+ };
2208+ pub const RegEnd : Reg = .{
2209+ .name = null ,
2210+ .func = null ,
2211+ };
2212+
2213+ /// Sets the given function as the value of a new global variable named `name`.
2214+ ///
2215+ /// From: `void lua_register(lua_State *L, const char *name, lua_CFunction f);`
2216+ /// Refer to: https://www.lua.org/manual/5.1/manual.html#lua_register
2217+ /// Stack Behavior: `[-0, +0, e]`
2218+ pub fn registerFunction (lua : * Lua , name : [:0 ]const u8 , function : CFunction ) void {
2219+ return c .lua_register (asState (lua ), asCString (name ), asCFn (function ));
2220+ }
2221+
2222+ /// Opens a library.
2223+ ///
2224+ /// When called with `name` equal to null, registers all functions in the list `functions` into the table on the
2225+ /// top of the stack. The list of functions *MUST* be terminated by the `Lua.RegEnd`
2226+ ///
2227+ /// When called with a non-null `name`:
2228+ /// * creates a new table `t`,
2229+ /// * sets `t` as the value of the global variable `name`,
2230+ /// * sets `t` as the value of package.loaded[libname],
2231+ /// * and registers on it all functions in the list `functions`.
2232+ ///
2233+ /// If there is a table in package.loaded[libname] or in variable libname, reuses this table instead of creating
2234+ /// a new one.
2235+ ///
2236+ /// Calls to `registerLibrary()` always leave the library table on the top of the stack.
2237+ ///
2238+ /// From: `void luaL_register(lua_State *L, const char *libname, const luaL_Reg *l);`
2239+ /// Refer to: https://www.lua.org/manual/5.1/manual.html#luaL_register
2240+ /// Stack Behavior: `[-(0|1), +1, m]`
2241+ pub fn registerLibrary (lua : * Lua , name : ? [:0 ]const u8 , functions : []const Lua.Reg ) void {
2242+ if (isSafeBuildTarget ) {
2243+ if (name == null ) {
2244+ assert (lua .isTable (-1 ));
2245+ }
2246+ assert (functions [functions .len - 1 ].name == null ); // When calling `lua.registerLibrary(name, functions)`, the functions must be terminated by `RegEnd`.
2247+ assert (functions [functions .len - 1 ].func == null ); // When calling `lua.registerLibrary(name, functions)`, the functions must be terminated by `RegEnd`.
2248+ }
2249+
2250+ return c .luaL_register (
2251+ asState (lua ),
2252+ if (name ) | p | @ptrCast (p .ptr ) else null ,
2253+ @ptrCast (functions .ptr ),
2254+ );
2255+ }
22042256};
22052257
22062258test "Lua can be initialized with an allocator" {
@@ -3251,16 +3303,18 @@ test "getTableIndexRaw" {
32513303 try std .testing .expectEqual (2 , lua .getTop ());
32523304}
32533305
3254- fn registeredFn (lua : * Lua ) callconv (.c ) i32 {
3255- lua .pushLString ("Galt, John" );
3256- return 1 ;
3257- }
3258-
32593306test "registering named functions" {
32603307 const lua = try Lua .init (std .testing .allocator );
32613308 defer lua .deinit ();
32623309
3263- lua .register ("regREG" , registeredFn );
3310+ const T = struct {
3311+ fn registeredFn (l : * Lua ) callconv (.c ) i32 {
3312+ l .pushLString ("Galt, John" );
3313+ return 1 ;
3314+ }
3315+ };
3316+
3317+ lua .registerFunction ("regREG" , T .registeredFn );
32643318 try lua .doString (
32653319 \\actual = regREG()
32663320 \\if actual == 'Galt, John' then
@@ -3272,6 +3326,80 @@ test "registering named functions" {
32723326 try std .testing .expectEqual (1 , lua .toIntegerStrict (-1 ));
32733327}
32743328
3329+ test "registering a library to the global namespace" {
3330+ const lua = try Lua .init (std .testing .allocator );
3331+ defer lua .deinit ();
3332+
3333+ const T = struct {
3334+ fn john (l : * Lua ) callconv (.c ) i32 {
3335+ l .pushLString ("Galt" );
3336+ return 1 ;
3337+ }
3338+ fn hank (l : * Lua ) callconv (.c ) i32 {
3339+ l .pushLString ("Reardon" );
3340+ return 1 ;
3341+ }
3342+ };
3343+
3344+ lua .registerLibrary (
3345+ "foo" ,
3346+ &[_ ]Lua.Reg {
3347+ .{ .name = "john" , .func = & T .john },
3348+ .{ .name = "hank" , .func = & T .hank },
3349+ Lua .RegEnd ,
3350+ },
3351+ );
3352+ try lua .doString (
3353+ \\john, hank = foo.john(), foo.hank()
3354+ \\if (john == 'Galt' and hank == 'Reardon') then
3355+ \\ return 1
3356+ \\end
3357+ \\return 0
3358+ );
3359+ try std .testing .expect (lua .isInteger (-1 ));
3360+ try std .testing .expectEqual (1 , lua .toIntegerStrict (-1 ));
3361+ }
3362+
3363+ test "registering a library to a table" {
3364+ const lua = try Lua .init (std .testing .allocator );
3365+ defer lua .deinit ();
3366+
3367+ const T = struct {
3368+ fn john (l : * Lua ) callconv (.c ) i32 {
3369+ l .pushLString ("Galt" );
3370+ return 1 ;
3371+ }
3372+ fn hank (l : * Lua ) callconv (.c ) i32 {
3373+ l .pushLString ("Reardon" );
3374+ return 1 ;
3375+ }
3376+ };
3377+
3378+ try lua .doString (
3379+ \\return function(lib)
3380+ \\ john, hank = lib.john(), lib.hank()
3381+ \\ if (john == 'Galt' and hank == 'Reardon') then
3382+ \\ return 1
3383+ \\ end
3384+ \\ return 0
3385+ \\end
3386+ );
3387+
3388+ lua .newTable ();
3389+ lua .registerLibrary (
3390+ null ,
3391+ &[_ ]Lua.Reg {
3392+ .{ .name = "john" , .func = & T .john },
3393+ .{ .name = "hank" , .func = & T .hank },
3394+ Lua .RegEnd ,
3395+ },
3396+ );
3397+
3398+ try lua .callProtected (1 , 1 , 0 );
3399+ try std .testing .expect (lua .isInteger (-1 ));
3400+ try std .testing .expectEqual (1 , lua .toIntegerStrict (-1 ));
3401+ }
3402+
32753403test "threads should share global state and not share local state" {
32763404 const lua = try Lua .init (std .testing .allocator );
32773405 defer lua .deinit ();
@@ -3488,10 +3616,17 @@ test "toCFunction should return expected function" {
34883616 const lua = try Lua .init (std .testing .allocator );
34893617 defer lua .deinit ();
34903618
3491- lua .pushCFunction (registeredFn );
3619+ const T = struct {
3620+ fn registeredFn (l : * Lua ) callconv (.c ) i32 {
3621+ l .pushLString ("Galt, John" );
3622+ return 1 ;
3623+ }
3624+ };
3625+
3626+ lua .pushCFunction (T .registeredFn );
34923627 const actual = lua .toCFunction (-1 );
34933628 try std .testing .expect (actual != null );
3494- try std .testing .expectEqual (registeredFn , actual );
3629+ try std .testing .expectEqual (T . registeredFn , actual );
34953630 lua .pop (1 );
34963631}
34973632
@@ -3516,6 +3651,13 @@ test "toPointer should return a non-null pointer for supported types" {
35163651 const lua = try Lua .init (std .testing .allocator );
35173652 defer lua .deinit ();
35183653
3654+ const T = struct {
3655+ fn registeredFn (l : * Lua ) callconv (.c ) i32 {
3656+ l .pushLString ("Galt, John" );
3657+ return 1 ;
3658+ }
3659+ };
3660+
35193661 lua .pushLString ("ASDF" );
35203662 try std .testing .expect (lua .toPointer (-1 ) != null );
35213663 lua .pop (1 );
@@ -3524,7 +3666,7 @@ test "toPointer should return a non-null pointer for supported types" {
35243666 try std .testing .expect (lua .toPointer (-1 ) != null );
35253667 lua .pop (1 );
35263668
3527- lua .pushCFunction (registeredFn );
3669+ lua .pushCFunction (T . registeredFn );
35283670 try std .testing .expect (lua .toPointer (-1 ) != null );
35293671 lua .pop (1 );
35303672
0 commit comments