Создать va_list динамически - PullRequest
16 голосов
/ 28 июля 2010

у меня есть функция

void foo(int cnt, va_list ap);

Мне нужно его использовать, но требование довольно строгое, число va_list меняется, и оно будет меняться во время выполнения. Что я хотел бы сделать, это:

создать va_list (которая ожидает char*) форму

QList<Contact*>

где Contact - определенный класс

class Contact
{
   public:
      QString getName();
   private: 
      QString m_name;

}; 

и я хотел бы заполнить в цикле va_list например:

for (int idx = 0; idx<contacts.count(); idx++)
{
    contacts.at(idx)->getName(); // this i would like to pass to va_list

}

Кто-нибудь знает, как я могу это сделать?

Ответы [ 7 ]

6 голосов
/ 29 июля 2010

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

Кажется, он работает в моей системе, Windows 7 с 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 );

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

4 голосов
/ 07 августа 2010

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

  • va_list (по крайней мере для Visual C ++) просто#define для char *
  • → аргументы не обязательно должны быть в стеке
  • → аргументы просто должны быть непрерывными в памяти
  • → не нужно использоватьассемблер и / или копирование (см. мой ответ «просто для удовольствия»: -)
  • → не нужно беспокоиться об очистке
  • эффективно!
  • протестировано на 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;
}

3 голосов
/ 28 июля 2010

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

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

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

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

1 голос
/ 28 июля 2010

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

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);
}
0 голосов
/ 20 июня 2014

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

ЭТО НЕ БУДЕТ РАБОТАТЬ:

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 голосов
/ 07 августа 2010

  • разрешение произвольного количества аргументов
  • к счастью 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);
}

0 голосов
/ 28 июля 2010

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

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

// РЕДАКТИРОВАТЬ

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

...