Создайте va_list динамично

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

void foo(int cnt, va_list ap);

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

создайте a 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

}

У кого-либо есть подсказка о том, как я мог сделать это?

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

6 ответов

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

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

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

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

0
ответ дан 30 November 2019 в 22:55
поделиться

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

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
ответ дан 30 November 2019 в 22:55
поделиться

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

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

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

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

3
ответ дан 30 November 2019 в 22:55
поделиться

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

Я сейчас пойду от стыда, что подумала о такой идее.

6
ответ дан 30 November 2019 в 22:55
поделиться

<просто для удовольствия>

  • разрешено произвольное количество аргументов
  • к счастью 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
ответ дан 30 November 2019 в 22:55
поделиться

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

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

4
ответ дан 30 November 2019 в 22:55
поделиться
Другие вопросы по тегам:

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