Подтвердить что ты не робот

Создать va_list динамически

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

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

}

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

4b9b3361

Ответ 1

То, что вы хотите сделать, это имитировать стек вызовов, чтобы вы могли передать построенный 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 );

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

Ответ 2

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

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

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

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

Ответ 3

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

  • va_list (по крайней мере, для визуального С++) просто #define для char *
  • → аргументы не должны находиться в стеке
  • → аргументы просто должны быть непрерывными в памяти
  • → нет необходимости использовать ассемблер и/или копирование (см. мой "просто для забавного ответа": -)
  • → нет необходимости беспокоиться об очистке.
  • эффективный!
  • проверено на w2k3 sp2 32bit + vС++ 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

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

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);
}

Ответ 5

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

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

//EDIT

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

Ответ 6

То, что вы пытаетесь использовать, - 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;
};

Ответ 7

< только для забавы >

  • разрешение произвольного количества аргументов
  • К счастью, sizeof (std:: wstring) является кратным sizeof (int)
  • проверено на w2k3 sp2 32bit + visual С++ 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);
}

</только для fun >