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

Я пытаюсь преобразовать некоторый код, чтобы он также компилировался в 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 * так что я получаю:

ошибка: ISO C++ запрещает приведение к типу массива '__va_list_tag [1]'

3 ответа

Решение

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

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

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

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

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

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

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


В продолжение комментария TonyK:

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

Не существует переносимого способа создания va_list.

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

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

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

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

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

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

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

Другие вопросы по тегам