强制 SWIG 生成 Lua 包装器以允许可变参数函数的额外参数

Force SWIG generated Lua wrapper to allow extra arguments for varargs functions

当我使用 SWIG 生成可变参数函数的包装器时,它会插入代码来检查传递给函数的参数的确切数量,例如:

%inline %{
void foobar(const char *fmt, ...) {}

生成的包装器总是插入:

SWIG_check_num_args("foobar",1,1)

我该如何解决这个问题并允许任意数量的参数大于或等于 1?

可以解决这个问题并生成在使用一系列 hack 传递额外参数时不会出错的代码。

本质上,hacks 归结为这个观察 - 在 SWIG_check_num_args 调用之前我们唯一可以控制的是局部变量的声明和初始化。然而,我们可以利用它来创建一个局部变量来覆盖全局变量,并根据它改变行为。

%module test

%runtime %{
static inline int SWIG_check_num_args_real(lua_State* L, const char *fn, int min_args, int max_args) {
    SWIG_check_num_args(fn, min_args, max_args);
    return 1;
fail:
    return 0;
}
#undef SWIG_check_num_args
#define SWIG_check_num_args(name,a,b) if (!SWIG_check_num_args_real(L,name,a,_global_is_varargs?1024:b)) goto fail; 
static const int _global_is_varargs=0;
%}

%typemap(in) (const char *fmt, ...) (const int _global_is_varargs=1) %{
    $typemap(in,const char *) // Default string stuff
    // TODO: Do some real work with the rest of the arguments here
%}

%inline %{
void foobar(const char *fmt, ...) {}
void boring(int i) {}
%}

为了让这个概念起作用,我们需要插入一些代码,用我们控制的宏替换默认宏 SWIG_check_num_args,最好不要完全重写宏,以防它以后发生变化。为此,我们设置了一个内联函数,以便我们可以有另一个 fail 标签供宏命中,但可以在稍后的 #undef.

之前应用宏的原始定义

有了这个内联函数,我们就可以将宏重新定义为一个稍微更有利的宏,它使用一个名为 _global_is_varargs 的变量来动态修改宏的第三个参数,并在我们需要时增加它在可变参数的情况下。 (1024 完全是任意的,INT_MAX 或某些特定于实现的限制同样有效)。

我们计划在阴影上使用的变量需要以魔术前缀 _global_ 开头,以避免 automatic appending of a suffix onto local variable names。由于一切都是 const,智能编译器可能也可以在没有额外运行时开销的情况下完成此操作。

然后我们可以编写实际创建局部变量的类型映射来屏蔽全局变量,并对 Lua 参数做一些实际工作。在我的示例中,我将其作为多参数类型映射来执行,但这可能不是绝对必要的。 (反正我喜欢完全匹配)。

有了这个我们就可以按预期调用函数(虽然它没有做任何有用的事情,但这是 preprocessor/FFI/ABI/gcc 扩展乐趣中的一个练习):

Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio
> m=require('test')
> m.boring()
stdin:1: Error in boring expected 1..1 args, got 0
stack traceback:
        [C]: in function 'test.boring'
        stdin:1: in main chunk
        [C]: in ?
> m.boring(1)
> m.foobar()
stdin:1: Error in foobar expected 1..1024 args, got 0
stack traceback:
        [C]: in function 'test.foobar'
        stdin:1: in main chunk
        [C]: in ?
> m.foobar('')
> m.foobar('',1)
> m.foobar('',1,2,3)

它不会破坏正常的检查并且仍然对函数调用参数强制执行一些合理的事情。