Можно обойти это и сгенерировать код, который не выдаст ошибку при передаче дополнительных аргументов, используя серию хаков.
По сути, хаки сводятся к этому наблюдению - единственное, что мы можем контролировать раньше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
, чтобы динамически изменить третий аргумент макроса и увеличить его, если мы находимся в некоторой ситуации типа varargs.(1024 является абсолютно произвольным, INT_MAX или некоторые определенные для реализации ограничения будут работать одинаково хорошо.)
Переменная, которую мы планируем для теневого копирования, должна начинаться с магического префикса _global_
, чтобы избежать автоматического добавления суффикса на имена локальных переменных .Поскольку все константно, умный компилятор, вероятно, может сделать это без дополнительных затрат времени выполнения.
Затем мы можем написать карты типов, которые фактически создают локальную переменную, чтобы маскировать глобальную переменную и выполнять некоторую реальную работу над аргументами Lua.Я сделал это в качестве примера с несколькими аргументами в моем примере, но это не является обязательным требованием.(В любом случае, мне понравилось точное соответствие).
Теперь мы можем вызывать функции, как и ожидалось (хотя это не дает ничего полезного, это упражнение в развлечении препроцессора / 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)
Он не нарушает обычные проверки и по-прежнему применяет что-то разумное в аргументах вызова функции.