Как создать va_list на GCC? - PullRequest
       6

Как создать va_list на GCC?

7 голосов
/ 26 декабря 2010

Я пытаюсь преобразовать некоторый код, чтобы он также компилировался в gcc (сейчас он компилируется только в MSVC).

Код, в котором я застрял, находится в функции псевдо-форматирования, котораяпринимает в качестве входных данных строку формата и ноль или более аргументов (const char *format, ...).Затем он обработает некоторые заполнителей, потребляющих некоторые аргументов, и передаст остальное в vsprintf вместе с новым динамически генерируемым va_list.

ЭтоФактический код для генерации нового va_list:

char *new_args = (char *) malloc(sum);
char *n = new_args;

for(int i = 0; i < nArgs; i++)
{
    int j   = order[i];
    int len = _getlen(types[j]);

    memcpy(n, args + cumulOffsets[j], len);
    n += len;
}

vsprintf(buffer, sFormat.c_str(), new_args);

В свою защиту я не писал и никогда не буду писать этот код.На самом деле, я думаю, что это одна из самых хакерских вещей, которые я видел за всю свою жизнь.

Однако эта функция очень сложная, очень старая и очень важная.Он также не изменялся годами (ну, кроме как сейчас), поэтому, хотя я хотел бы переписать его с нуля, я не могу оправдать время, которое потребуется, плюс ошибки, которые он может внести.

ИтакМне нужен способ сделать то же самое на GCC .. Но там va_list не char *, поэтому я получаю:

error: ISO C++ forbids casting to an array type '__va_list_tag [1]'

Ответы [ 3 ]

4 голосов
/ 26 декабря 2010

Я немного растерялся.Зачем вам новый динамически сгенерированный va_list ?Почему бы просто не использовать старый?

Я полагаю, vsnprintf () использует текущий объект va_list (если это можно так назвать).Таким образом, вы можете va_start () , использовать нужные аргументы через va_arg () , а затем передать оставшиеся аргументы через va_list в vsnprintf() , а затем позвоните va_end () .

Я что-то упустил?Почему глубокая копия?

И если вам нужна глубокая копия, почему бы не va_start () fresh, удалить нужные аргументы с помощью va_arg () изатем передайте полученный va_list объект vsnprintf () .

(Каждый вызов va_arg изменяет объект va_list так что следующий вызов возвращает следующий аргумент.)

В качестве альтернативы, вы можете просто использовать va_copy () .(Хотя обязательно следуйте за ним с соответствующим va_end () .)

Приложение: Также обратите внимание, что эти макросы va_ основаны на стандартах C89 и C99.GNU g ++ будет их поддерживать.Microsoft несколько более ограничена.


Вслед за Комментарий TonyK :

То, что я сказал выше, работает, если вы тянете первыйN предметов из va_list .Если вы вытаскиваете предметы из середины, это сложнее.

Нет переносного пути к конструкции a va_list .

Однако вы можете отделить строку формата, использовать ее для определения типов объектов (double, float, int и т. Д.) И распечатать каждый из них отдельно со своей строкой формата (подраздел исходной строки формата),Многократные вызовы snprintf () вызовут некоторые накладные расходы.Но если эта процедура не вызывается слишком часто, она должна быть жизнеспособной.

Вы также можете распечатать подразделы исходной строки формата с соответствующим образом созданным va_list .Другими словами, первый вызов vsnprintf () печатает элементы 1..3, вторые элементы 5..7, третий 10..13 и т. Д. (As vsnprintf () будет игнорировать дополнительные элементы в va_list сверх того, что ему нужно. Вам просто нужна серия соответствующих форматных строк-фрагментов и добавление элементов из va_list с помощью va_arg () по мере необходимости для каждого vsnprintf () вызова.)

1 голос
/ 27 декабря 2010

Недостаточно контекста, чтобы понять, что вы пытаетесь сделать здесь, но если вам нужно скопировать va_list, вы можете использовать стандартную функцию C99 va_copy, которую поддерживает gcc (но у меня нет Идея, если MS поддерживает это).

0 голосов
/ 22 апреля 2014

Есть способ сделать это, это не красиво:

union {
      char *pa;
      va_list al;
      } au;

....
au.pa = new_args;
vsprintf(buffer, sFormat.c_str(), au.al);

Использование объединения вместо преобразования является уродливым, но вы не можете выполнить приведение, если va_list является типом массива.

...