Стандартный способ манипулировать аргументами? - PullRequest
7 голосов
/ 05 февраля 2010

Это странный вопрос, но есть ли стандартный способ манипулирования содержимым va_list перед передачей его другой функции? Например, предположим, у меня есть две функции, sum и vsum:

int vsum(int n, va_list ap) {
    int total = 0;
    for (int i = 0; i < n; ++i) {
        total += va_arg(n, int);
    return total;
}

int sum(int n, ...) {
    va_list ap;
    va_start(ap, n);
    int total = vsum(n, ap);
    va_end(ap);
    return total;
}

Если я вызову sum как sum(4, 1, 2, 3, 4), я ожидаю получить результат 10. Теперь давайте предположим, что вместо прямого вызова vsum sum вызывает промежуточную функцию vsum_stub, которая выполняет следующие действия:

int vsum_stub(int n, va_list ap) {
    va_list temp_ap;
    va_copy(temp_ap, ap);
    for (int i = 0; i < n; ++i) {
        int *arg = &va_arg(ap, int);
        *arg += 2;
    }
    va_end(temp_ap);
    return vsum(n, ap);
}

Теперь, когда я вызываю sum(4, 1, 2, 3, 4), я должен получить результат 20, поскольку vsum_stub увеличивает все значения в va_list на 2. Конечно, это не компилируется, так как вы не можете взять адрес результата va_arg. Есть ли другой способ сделать это? Я работаю в C99.


Справка:

Я работаю над библиотекой, которая выполняет перевод некоторых указателей, чтобы данные могли храниться в куче в более эффективном формате. Программы компилируются с помощью специального преобразования, которое преобразует вызовы библиотечных функций, таких как printf, в мои собственные функции-заглушки (например, hc_printf). hc_printf необходимо перевести все аргументы указателя (строки, предназначенные для %s) перед передачей аргументов в реальную функцию printf.

Редактировать: Вот пример кода. Допустим, у нас есть строка foo. foo динамически выделяется с помощью модифицированной версии malloc, которая возвращает поддельный указатель. Компилятор модифицирует программу, чтобы она могла работать с фальшивыми указателями. Так что это работает:

char *foo = fake_malloc(4);
fake_strcpy(foo, "foo");

Я хочу написать fake_vprintf функцию, подобную этой (в псевдокоде):

int fake_vprintf(const char *format, va_list args) {
    for each pointer argument p in args
        translate p to q, a real pointer to contiguous memory
        replace p with q in args
    }
    return vprintf(format, args);
}

Программа будет вызывать fake_vprintf точно так же, как исходный vprintf, используя фальшивый указатель. fake_vprintf переводит фальшивый указатель в реальный указатель, который может использовать реальный vprintf.

Ответы [ 2 ]

3 голосов
/ 06 февраля 2010

Вы, вероятно, не можете использовать va_list независимо от платформы. Вам нужно посмотреть, как ваша среда определяет va_list в stdarg.h, а затем написать свои собственные инструменты для работы с ним.

Например, если va_list это просто (char *), вы можете делать с ним все что угодно.

// add 1000 to the integer stored on the stack and advance va_list
*(int *)va_list += 1000;
va_list += sizeof(int);

Вы говорите компилятору, что вы хотите, чтобы он считал va_list указателем на int (через int * cast), затем принимаете значение (*) и добавляете к нему 1000 (+ = 1000). Теперь переместите указатель va_list к следующему аргументу в стеке.

2 голосов
/ 06 февраля 2010

Ага, как я понимаю, ваша проблема заключается в создании нового аргумента va_list для передачи стандартным vprintf функциям. Что, в свою очередь, потребует от вас изменения каждого члена списка. Однако, поскольку для такого списка нет операции извлечения / редактирования / вставки по элементам, вы застряли.

Я действительно не вижу никакого способа сделать это. Конечно, вы можете создать vprintf применить преобразования in situ , по одному аргументу за раз. Мое предложение будет таким: Переопределить все такие стандартные функции библиотеки - во всяком случае, вы пишете оболочки. Это включает в себя некоторую работу, но вы уже выполняете часть ее с hc_printf и т. Д., Так почему бы не пройти всю дистанцию ​​(и угадать, что сэкономит на вызове функции!).

...