C / C ++ va_arg - есть ли способ пропустить аргумент? - PullRequest
5 голосов
/ 09 мая 2011

Я хочу добавить функциональность в sprintf ().В частности, я хочу иметь возможность передавать ему свои собственные типы данных POD, но я не уверен, как это сделать.

Предположительно, если вы создаете va_list, вы можете передать его в vsprintf ()и пусть это сделает тяжелую работу за вас - но мне все еще нужно получить доступ к va_list и самостоятельно извлечь элементы перед передачей va_list в vsprintf ().

Например, предположим следующий код:

struct mypod {
    int somedata;
}; // just for example, you know

// somewhere else in the code...
mypod mp;
mp.somedata = 5325;
my_sprintf(myChrPtr, "%z", mp);

С новым кодом% z, соответствующим моему новому типу данных.

Я понимаю, что только указатели и структуры POD могутпройти, это не имеет большого значения.Однако мне интересно, что произойдет, если я доберусь до конца va_list (получая аргументы с помощью va_arg ()) и затем передам его vsprintf ()?

Спасибо!

Ответы [ 2 ]

1 голос
/ 09 мая 2011

Учитывая, что va_arg должен вызываться в правильном порядке и с правильным типом для правильного поведения, я думаю, что вам нужно потреблять fmt по частям, обрабатывая части, которые вам нужны, и передавая оставшуюся часть vsprintf:

void
mysprintf(char *dst, char *fmt, ...)
{
    char *p = fmt;
    va_list va;

    va_start(va, fmt);
    do {
        char *q = strchr(p, '|');
        if (q) {
            /* really should take const char *fmt and not write on it like this... */
            *q++ = '\0';
        }
        dst += vsprintf(dst, p, va);  /* let vsprintf do a subset */
        if (q) {
            dst += sprintf(dst, "%d", va_arg(va, int));  /* consume 1 int */
        }
        p = q;
    } while (p);

    va_end(va);
}


strcpy(fmt, "%s|%s|%s");  /* mutable fmt */
mysprintf(buf, fmt, "hello", 7, "world", 8, "!");

В моем примере я просто взял |, чтобы быть похожим на %d, но реальный пример с дополнительными %... побегами будет намного более сложным.

1 голос
/ 09 мая 2011

Сделайте два va_list с и используйте только один, чтобы сделать то, что вам нужно, затем передайте другой на vsprintf или как угодно:

va_list l1 = va_start(final_arg);
va_list l2 = va_start(final_arg);

// do stuff with l1 and l2 will be unaffected

vsprintf(/*using l2*/);

Помните, что va_list на самом деле является только указателем на место в стеке (я думаю, это зависит от реализации, но именно так и происходит). va_arg возвращает объект, на который указывает va_list, приведенный к указанному типу, и увеличивает указатель на sizeof(thetype). Так что, если вы получите два указателя и оставите один без изменения, вы можете просто передать его другой функции.

Надеюсь, я не правильно понял вопрос.

...