@@ -393,35 +393,59 @@ pub const Lua = struct {
393393 self .state ().pop (1 ); // Pop table
394394 }
395395
396- /// Sets a function in the table as a closure with upvalues .
396+ /// Sets a function with upvalues in the table as a Lua C closure .
397397 ///
398- /// Similar to `set(key, func)` but allows creating Lua closures with captured values.
398+ /// Similar to `set(key, func)` but allows creating closures with captured values.
399399 /// Upvalues are values that are captured and accessible to the function.
400- /// The function must accept the upvalues as its first parameter.
400+ /// The function must accept upvalues as its first parameter using the `Upvalues(T)` wrapper type .
401401 ///
402402 /// WARNING: When using light user data pointers as upvalues, the user is responsible
403403 /// for ensuring the pointer remains valid for the lifetime of the closure.
404404 ///
405405 /// Parameters:
406406 /// - `key`: The table key where the closure will be stored
407407 /// - `upvalues`: Values to be captured as upvalues (single value or tuple)
408- /// - `func`: A Zig function that accepts upvalues as its first parameter
408+ /// - `func`: A Zig function with `Upvalues(T)` as its first parameter
409409 ///
410410 /// Examples:
411411 /// ```zig
412- /// // Single upvalue
413- /// fn add(increment: i32, x: i32) i32 { return x + increment; }
414- /// try table.setClosure("add5", .{5}, add);
412+ /// fn add(upv: Upvalues(i32), x: i32) i32 {
413+ /// return upv.value + x;
414+ /// }
415+ /// try table.setClosure("add5", 5, add);
415416 ///
416- /// // Multiple upvalues (tuple)
417- /// fn scaledAdd(upvals: struct { f32, f32 }, x: f32) f32 {
418- /// return x * upvals[0] + upvals[1];
417+ /// fn transform(upv: Upvalues(struct { scale: f32, offset: f32 }), x: f32) f32 {
418+ /// return x * upv.value.scale + upv.value.offset;
419419 /// }
420- /// try table.setClosure("transform ", .{ 2.0, 10.0 }, scaledAdd );
420+ /// try table.setClosure("scale2add10 ", .{ .scale = 2.0, .offset = 10.0 }, transform );
421421 /// ```
422422 ///
423423 /// Errors: `Error.OutOfMemory` if stack allocation fails
424424 pub fn setClosure (self : Table , key : anytype , upvalues : anytype , func : anytype ) ! void {
425+ const FuncType = @TypeOf (func );
426+ const func_info = @typeInfo (FuncType );
427+
428+ if (func_info != .@"fn" ) {
429+ @compileError ("Third parameter must be a function" );
430+ }
431+
432+ const arg_tuple = std .meta .ArgsTuple (FuncType );
433+ const arg_fields = std .meta .fields (arg_tuple );
434+
435+ if (arg_fields .len == 0 ) {
436+ @compileError ("Function must have at least one parameter (Upvalues)" );
437+ }
438+
439+ const FirstParamType = arg_fields [0 ].type ;
440+ const first_param_info = @typeInfo (FirstParamType );
441+
442+ if (first_param_info != .@"struct" or
443+ ! @hasDecl (FirstParamType , "is_upvalues" ) or
444+ ! FirstParamType .is_upvalues )
445+ {
446+ @compileError ("First parameter of the function must be an Upvalues type" );
447+ }
448+
425449 const upvalues_info = @typeInfo (@TypeOf (upvalues ));
426450 const upvalue_count = if (upvalues_info == .@"struct" and upvalues_info .@"struct" .is_tuple )
427451 upvalues_info .@"struct" .fields .len
@@ -443,12 +467,7 @@ pub const Lua = struct {
443467 }
444468
445469 // Create the closure with upvalues
446- const FuncType = @TypeOf (func );
447- const arg_fields = std .meta .fields (std .meta .ArgsTuple (FuncType ));
448- if (arg_fields .len == 0 ) {
449- @compileError ("Closure function must have at least one parameter for upvalues" );
450- }
451- const trampoline : State.CFunction = stack .createFunc (self .ref .lua , func , arg_fields [0 ].type );
470+ const trampoline : State.CFunction = stack .createFunc (self .ref .lua , func );
452471 self .state ().pushCClosureK (trampoline , @typeName (@TypeOf (func )), @intCast (upvalue_count ), null );
453472
454473 self .state ().setTable (-3 ); // Set table[key] = closure and pop key and value
@@ -1071,6 +1090,25 @@ pub const Lua = struct {
10711090 }
10721091 };
10731092
1093+ /// Generic wrapper for upvalues passed to Lua C closure functions.
1094+ ///
1095+ /// This type enables functions to receive upvalues in a type-safe manner when registered
1096+ /// as Lua C closures with `table.setClosure()`. The upvalues are automatically injected
1097+ /// when the function is called from Lua.
1098+ ///
1099+ /// `Upvalues(T)` must be used as the first parameter of the function.
1100+ ///
1101+ /// See `Table.setClosure()` documentation for usage examples.
1102+ pub fn Upvalues (comptime T : type ) type {
1103+ return struct {
1104+ value : T ,
1105+
1106+ /// Marker field to distinguish from regular types
1107+ pub const is_upvalues = true ;
1108+ pub const UpvalueType = T ;
1109+ };
1110+ }
1111+
10741112 /// Variadic arguments iterator for functions accepting variable number of arguments from Lua.
10751113 ///
10761114 /// This type enables Zig functions to accept any number of arguments from Lua
0 commit comments