Как передать функцию с различным числом аргументов в качестве аргумента в C? [Дубликат]

Поскольку я нашел это во время поиска в Google, вы должны знать, что этого простого правила достаточно, чтобы предотвратить это сообщение:

react/jsx-uses-react

Набор правил react/recommended добавляет многие другие правила вы можете не захотеть.

14
задан Keith Pinson 31 December 2012 в 21:08
поделиться

7 ответов

В зависимости от компилятора, что такое тип va_list, каковы макросы va_start и va_end. Вы не можете сделать это стандартным способом. Вам нужно будет использовать конструкцию, специфичную для компилятора.

Возможно, вы можете изменить функцию foo? Если это так, то сделайте это обратно - конвертируйте va_list в QList и сделайте «foo» принять QList.

// EDIT

Затем посмотрите, что такое тип va_list, что va_start и va_end макросы находятся в вашем конкретном компиляторе. Затем создайте свой va_list таким образом, чтобы эти макросы работали над ним.

0
ответ дан adf88 21 August 2018 в 10:35
поделиться
  • 1
    Я действительно мог этого ожидать. Я разрабатываю приложение QT для Windows IDE: Visual Studio 2005. – user404251 28 July 2010 в 10:52
  • 2
    Я не могу этого сделать, это функция, которая отображается LIB :). У меня нет исходного кода. – user404251 28 July 2010 в 10:56
  • 3
    Я не могу этого сделать, это функция, которая отображается LIB :). У меня нет исходного кода. – user404251 28 July 2010 в 10:56
  • 4
    Я сделал некоторые изменения, снова прочитал мой ответ. – adf88 28 July 2010 в 11:12

Если количество элементов в списке ограничено, я бы пошел на ручную отправку в зависимости от количества элементов.

void call_foo(int count, ...) {
    va_list args;
    va_start(args, count);
    foo(count, args);
    va_end(args);
}

switch (contacts.count()) {
    case 0: return call_foo(contacts.count());
    case 1: return call_foo(contacts.count(),
                            contacts.at(0)->getName());
    case 2: return call_foo(contacts.count(),
                            contacts.at(0)->getName(),
                            contacts.at(1)->getName());
    case 3: return call_foo(contacts.count(),
                            contacts.at(0)->getName(),
                            contacts.at(1)->getName(),
                            contacts.at(2)->getName());
    default: /* ERROR HERE, ADD MORE CASES */ return call_foo(0);
}
1
ответ дан Didier Trosset 21 August 2018 в 10:35
поделиться
  • 1
    Там причина для этого не самый эффективный способ сделать это: я могу иметь 1-999 элементов Contact, и это выглядело бы довольно уродливо. Я надеялся найти более эффективное решение. Rgds Lukasz – user404251 28 July 2010 в 10:45
  • 2
    Я не уверен, что более эффективный вызов функции с параметрами 999. – Didier Trosset 28 July 2010 в 11:02
  • 3
    Вы уверены, что функция foo в библиотеке сможет обрабатывать va_list с параметрами 999? – Didier Trosset 28 July 2010 в 15:24
  • 4
    На самом деле я думаю, что только 64 параметра гарантированы C & amp; C ++. Если даже это слишком уродливо, вы всегда можете попробовать библиотеку препроцессора boost для повторения кода. – Joe D 29 July 2010 в 14:56

То, что вы пытаетесь использовать, - alloca. Объект va_list не может хранить переменные, вызов функции хранит их, и вы можете обращаться к нему только через va_list. Эти переменные действительны только во время вызова, и после этого они получают ovverwriten.

ЭТО НЕ РАБОТАЕТ:

va_list func(int dummy, ...)
{
   va_list result;
   va_start(result, dummy);
   return result;
}

Чтобы выделить память в стеке, без необходимости писать переменную функции используют alloca. Он работает более или менее как malloc, но вам не нужно вызывать free, он автоматически освобождается, когда вы покидаете область действия.

int * local = (int *) alloca(3 * sizeof(int));
local[0] = 10;
local[1] = 20;
local[2] = 30;

Это принципиально то же самое, что писать

int local[3];
local[0] = 10;
local[1] = 20;
local[2] = 30;

Но с alloca 3 не обязательно должна быть константа. Опять же, вы можете использовать его только внутри области приложения, поэтому не возвращайте его из функции.

, если вы хотите из va_list, это несколько типов в одном списке, подумайте о том, как написать такой союз:

union variant
{
    int          i;
    unsigned int u;
    float        f;
    double       d;
    const char * s;
    void *       v;
};
0
ответ дан Evan Dark 21 August 2018 в 10:35
поделиться

То, что вы хотите сделать, это имитировать стек вызовов, чтобы вы могли передать построенный va_list в foo (). Это довольно специфично для компилятора (и предупреждения, существуют различия между даже 32- и 64-разрядными компиляторами). Следующий код предназначен для ТОЛЬКО ДЛЯ РАЗВЛЕЧЕНИЙ! as (если он работает даже в вашей системе), он подвержен поломке. С его помощью я использую плоский буфер памяти и заполняю его счетчиками и цепочкой символов. Вы можете заполнить его соответствующим образом указателями на свои строки и передать их.

Кажется, что это работает в моей системе Windows 7 w / Visual Studio 2008 только для 32-разрядных приложений.

* ПОСЛЕДУЮЩИЙ КОД ИДЕИ !!! *

#define PSEUDOSTACKSIZE ( sizeof(int) + 999 * sizeof(const char*) )
#pragma pack( push,1 )
union PSEUDOSTACK
{
    int count;
    char data[PSEUDOSTACKSIZE];
};
#pragma pack( pop )

void foo( int count, va_list args )
{
    for ( int i = 0; i < count; i++ )
    {
        char *s = va_arg( args, char* );
        printf( "%s\n", s);
    }
}

void bar( PSEUDOSTACK data, ... ) 
{ 
    va_list args; 
    va_start(args, data.count); 
    foo( data.count, args);
    va_end(args); 
} 
// And later on, the actual test case code.
PSEUDOSTACK barData;
barData.count = 999;
char *p = barData.data + sizeof(int);
for ( int i = 0; i < 999; i++, p += sizeof(char*) )
{
    *reinterpret_cast<char**>(p) = "ThisIsABadIdea";
}
bar( barData );

Теперь я позову свою голову в стыде за то, что подумал о такой идее.

6
ответ дан Joe Marley 21 August 2018 в 10:35
поделиться
  • 1
    Не только плохая идея, возможно даже противоречащая стандарту C (C99: 7.15.1.1.2, 7.15.1.3.2 и, возможно, 7.15.1.4.2), возможно неопределенное поведение. Не совсем уверен. – Aconcagua 20 July 2016 в 15:32

В вашем вопросе отмечен C ++, и есть хорошие способы (например, потоки), чтобы полностью исключить varargs на C ++.

Это отличный пример того, почему va_args может вызвать боль. Если у вас есть шанс изменить подпись foo, это ваш лучший вариант. Взяв std::vector<std::string> вместо va_list, вы просто решите свою проблему там.

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

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

3
ответ дан Mark B 21 August 2018 в 10:35
поделиться

& lt; just for fun & gt;

  • , позволяющий произвольное количество аргументов
  • , к счастью, sizeof (std :: wstring) является кратным sizeof (int)
  • проверен на w2k3 sp2 32bit + visual c ++ 2010

#include <stdarg.h>
#include <string>
#include <vector>
#include <iostream>

#define N 6 // test argument count

void foo(int n, ...);

int main(int, char*[])
{
    std::vector strings;
    std::wstring s(L"a");
    int i(0);

    // create unique strings...
    for (; i != N; ++i)
    {
        strings.push_back(s);
        ++s.front();
    }

    int n_stack_strings(N*sizeof(std::wstring)),    // space needed for strings
        n_stack(sizeof(int)+n_stack_strings);   // overall stack space...needed for cleanup

    __asm sub esp, n_stack_strings  ; reserve stack space

    std::wstring* p_stack(0);

    __asm mov p_stack, esp  ; get stack pointer

    std::wstring* p(p_stack);
    std::vector<std::wstring>::iterator string(strings.begin());

    // copy to stack
    for (; string != strings.end(); ++string, ++p)
        new (p) std::wstring(*string);
    __asm push N    ; argument count...arguments right to left (__cdecl)
    __asm call foo
    // cleanup
    for (p = p_stack; p != p_stack+N; ++p)
        p->~basic_string();
    __asm add esp, n_stack  ; caller has to cleanup the stack (__cdecl)
    return 0;
}

void foo(int n, ...)
{
    int i(0);
    va_list marker;

    va_start(marker, n);
    for (; i != n; ++i)
        std::wcout << va_arg(marker, std::wstring) << std::endl;
    va_end(marker);
}

& lt; / just for fun & gt;

4
ответ дан slow 21 August 2018 в 10:35
поделиться
  • 1
    Наверное, не портативный. В GCC va_list используется typedef для __builtin_va_list. – Henri Menke 4 June 2018 в 06:59

... хммм ... может быть, не переносится ... наверняка не приятно ... но может решить проблему yor ...

  • va_list (по крайней мере, для визуального c ++) просто #define для char *
  • & rarr; аргументы не должны находиться в стеке
  • & rarr; аргументы должны просто быть непрерывными в памяти
  • & rarr; нет необходимости использовать ассемблер и / или копирование (см. мой «просто для забавного ответа»: -)
  • & rarr;
  • проверено на w2k3 sp2 32bit + vc ++ 2010

#include <stdarg.h>
#include <string>
#include <vector>
#include <iostream>

#define N 6 // test argument count

void foo(int n, va_list args);

int main(int, char*[])
{
    std::vector<std::wstring> strings;
    std::wstring s(L"a");
    int i(0);

    // create unique strings...
    for (; i != N; ++i)
    {
        strings.push_back(s);
        ++s.front();
    }
    foo(N, reinterpret_cast<va_list>(strings.data()));
    return 0;
}

void foo(int n, va_list args)
{
    int i(0);

    for (; i != n; ++i)
        std::wcout << va_arg(args, std::wstring) << std::endl;
}

4
ответ дан slow 21 August 2018 в 10:36
поделиться
  • 1
    Наверное, не портативный. В GCC va_list используется typedef для __builtin_va_list. – Henri Menke 4 June 2018 в 06:59
Другие вопросы по тегам:

Похожие вопросы: