Поэтому я делал собственный синтаксический анализатор для языка сценариев, и я хотел иметь возможность передавать только аргументы эллипсов. Мне не нужна или не нужна начальная переменная, однако Microsoft и C, похоже, хотят чего-то другого. К вашему сведению, см внизу для информации.
Я посмотрел на определения va_ *
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
и часть, которую я не хочу, v в va_start. Как небольшой опыт, я хорошо разбираюсь в goasm и знаю, как работает стек, поэтому я знаю, что здесь происходит. Мне было интересно, если есть способ получить базу стека функций без использования встроенной сборки.
У меня были идеи:
#define im_va_start(ap) (__asm { mov [ap], ebp })
и т.д … но на самом деле я чувствую, что это грязно, и я делаю это неправильно.
struct function_table {
const char* fname;
(void)(*fptr)(...);
unsigned char maxArgs;
};
function_table mytable[] = {
{ "MessageBox", &tMessageBoxA, 4 } };
… некоторая функция, которая сортирует через const char *, переданную ей, чтобы найти соответствующую функцию в mytable, и вызывает tMessageBoxA с параметрами. Кроме того, аргумент maxArgs предназначен для проверки того, что отправляется правильное количество параметров. У меня есть личные причины, по которым я не хочу отправлять это в функцию, но пока мы можем просто сказать, что это потому, что мне любопытно.
Это всего лишь пример; Пользовательские библиотеки — это то, что я буду реализовывать, чтобы они не просто вызывали WinAPI.
void tMessageBoxA(...) {
// stuff to load args passed
MessageBoxA(arg1, arg2, arg3, arg4);
}
Я использую соглашение о вызовах __cdecl, и я искал способы надежного получения указателя на основание стека (не на верх), но, похоже, я не могу его найти. Кроме того, меня не беспокоит безопасность функций или проверка типов.
редактировать: спасибо за ввод, кажется, это было невозможно.
Мое исправление закончилось тем, что
#define im_va_start(ap) {\
__asm push eax\
__asm mov eax, ebp\
__asm add eax, 8h\
__asm mov ap, eax\
__asm pop eax\
}
И тогда я могу продолжить как обычно.
Что касается того, почему мне это нужно, я делаю некоторые (уникальные) трюки read: unsafe и использую массив struct с указателем на функцию, как определено выше. Поскольку каждая функция уникальна, и большинство из них из моей пользовательской библиотеки, они имеют … различное поведение. Я не знаю, как это объяснить, но я выпущу источник, когда закончу POC.
Я не очень беспокоюсь о переносимости, так что придется работать. Также для подсчета аргов я сделал:
#define im_va_count(ap, num, t) {\
for(num = 0; *(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) > 0; ++num){ }\
--num;\
im_va_start(argptr);\
}
который работает для меня. Если кому-то интересно …
К сожалению, это невозможно, стандарт C говорит:
Функция может быть вызвана с переменным числом аргументов разных типов. Как
описанный в 6.9.1, его список параметров содержит один или несколько параметров.
И ...
не считается «одним или несколькими параметрами». В дальнейшем,
va_start
макрос должен быть вызван перед любым доступом к безымянным аргументам.
Что это надо называть как
va_start(va_list, parmN)
И это
Параметр
parmN
является идентификатором самого правого параметра в переменной
список параметров в определении функции (тот, что перед, ...
).
Таким образом, как вы можете видеть, у вас не может быть функции с переменными числами без хотя бы одного параметра перед многоточием в стандартном C ++. Неподвижный сборочный трюк — самый близкий, который вы можете прийти.
Других решений пока нет …