2010-07-28 9 views
13

Mam funkcjiTworzenie va_list dynamicznie

void foo(int cnt, va_list ap); 

muszę go używać, ale warunkiem jest dość ścisły, ilość va_list zmieniać i będzie to zmienić w czasie wykonywania. Co chciałbym zrobić, to:

stworzyć va_list (która oczekuje char*) formularz

QList<Contact*> 

gdzie Contact jest zdefiniowana klasa

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

}; 

i chciałbym, aby wypełnić w pętli va_list na przykład:

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

} 

Czy ktoś ma pojęcie o tym, jak mogę to zrobić?

+1

można zmienić funkcję „foo”? – adf88

+2

możliwy duplikat http://stackoverflow.com/questions/988290/populating-a-va-list – Hasturkun

+0

zdecydowanie duplikat pod względem poprawnej odpowiedzi. – bmargulies

Odpowiedz

0

Jeśli liczba elementów na liście jest ograniczona, wybrałbym ręczną wysyłkę w zależności od liczby elementów.

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

Powodem tego nie jest najbardziej efektywny sposób, aby to zrobić: Mogę mieć 1-999 elementów Kontakt i to wyglądałoby dość brzydko. Miałem nadzieję znaleźć lepsze rozwiązanie. Rgds Lukasz – user404251

+0

Nie jestem pewien, czy * lepiej wydajne * wywołanie funkcji z 999 parametrami. –

+0

Czy jesteś pewien, że funkcja foo w bibliotece będzie w stanie obsłużyć va_list z 999 parametrami? –

0

Zależy od kompilatora co to jest typ va_list, jakie są makra va_start i va_end. Nie możesz tego zrobić w standardowy sposób. Musiałbyś użyć konstrukcji specyficznej dla kompilatora.

Może możesz zmienić funkcję "foo"? Jeśli tak, to uczyń to odwrotnie - skonwertuj va_list do QList i ustaw "foo", aby zaakceptować QList.

// EDIT

potem zobaczymy co typ va_list to, co makra va_start i va_end są w konkretnej kompilator. Następnie zbuduj listę va_ w taki sposób, aby te makra na niej działały.

+0

Mogłem się tego spodziewać. Rozwijam aplikację QT dla Windows IDE: Visual Studio 2005. – user404251

+0

Nie mogę tego zrobić, jeśli funkcja jest odsłonięta przez LIB :). Nie mam kodu źródłowego dla tego. – user404251

+0

Nie mogę tego zrobić, jest to funkcja odsłonięta przez LIB :). Nie mam kodu źródłowego dla tego. – user404251

3

Twoje pytanie zostało oznaczone jako C++ i istnieją dobre sposoby (takie jak strumienie), aby całkowicie uniknąć varargs w C++.

Jest to doskonały przykład, dlaczego va_args może powodować ból. Jeśli masz jakąkolwiek szansę na zmianę podpisu foo, to twoja najlepsza opcja. Robienie std::vector<std::string> zamiast va_list rozwiąże problem właśnie tutaj.

Jeśli foo znajduje się w bibliotece zewnętrznej, której nie można zmienić, moją następną sugestią będzie znalezienie innej biblioteki.

Jeśli żadna z nich nie jest opcją, wydaje się, że powinien istnieć sposób rekurencyjnego tworzenia listy połączeń za pomocą va_list, ale nie mogłem wymyślić, jak wykonać tę pracę.

5

To, co chcesz zrobić, to symulować stos wywołań, dzięki czemu możesz przekazać skonstruowaną listę va_ do foo(). Jest to raczej specyficzne dla kompilatora (i ostrzeżenie, istnieją różnice między nawet kompilatorami 32- i 64-bitowymi). Poniższy kod jest dostępny tylko dla ZAŁĄCZNIKÓW! as (jeśli działa nawet w twoim systemie) jest podatny na złamanie. Dzięki niemu używam płaskiego bufora pamięci i zapełniam go liczbą i serią ciągów znaków. Możesz wypełnić je stosownymi wskaźnikami do twoich strun i przekazać je.

Wydaje się działać na moim systemie, Windows 7 w Visual Studio 2008, tylko dla aplikacji 32-bitowych.

* NIEPOPRAWNY KOD POMYSŁU !!! *

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

będę teraz iść powiesić moją głowę ze wstydu do myślenia takiego pomysłu.

+0

Nie tylko zły pomysł, być może nawet sprzeczny ze standardem C (C99: 7.15.1.1.2, 7.15.1.3.2 i ewentualnie 7.15.1.4.2), prawdopodobnie nieokreślone zachowanie. Ale nie jestem do końca pewien. – Aconcagua

-1

< tylko dla zabawy >

  • umożliwiający arbitralne argumentem liczyć
  • na szczęście sizeof (std :: wstring) jest wielokrotnością sizeof (int)
  • testowany na 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); 
} 
 

</tylko dla zabawy >

3

... hmmm ... może nie przenośny ... na pewno nie jest to miłe ... ale może rozwiązać problemu yor ...

  • va_list jest (przynajmniej dla Visual C++) tylko #define dla char *
  • → argumenty nie muszą być na stosie
  • → argumenty są wymagane tylko w sposób ciągły w pamięci
  • → ma potrzeby korzystania z assembler i/lub kopiowanie (patrz moja „tylko dla dobrej zabawy ":-)
  • → nie musisz się martwić oczyszczaniem
  • skuteczny!
  • testowany na 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; 
} 
 
0

Co ty próbujesz użyć jest alloca. Obiekt va_list nie może przechowywać zmiennych, wywołanie funkcji przechowuje je, a dostęp do niego można uzyskać tylko poprzez va_list. Te zmienne są ważne tylko podczas połączenia, a później zostają zapisane.

to nie będzie działać:

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

przydzielić pamięci na stosie, bez konieczności pisania o zmiennej liczbie argumentów funkcji użyć alloca. Działa mniej więcej tak jak malloc, ale nie musisz zadzwonić pod numer free, automagicznie uwalnia się, gdy opuszczasz lunetę.

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

To zasadniczo takie same jak pisanie

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

ale z alloca 3 nie musi być stała. Znowu możesz go używać tylko wewnątrz otaczającego zakresu, więc nie zwracaj go z funkcji, aby , a nie.

jeśli to, co chcesz z va_list jest wiele typów w jednym liście rozważyć napisanie unii takiego:

union variant 
{ 
    int   i; 
    unsigned int u; 
    float  f; 
    double  d; 
    const char * s; 
    void *  v; 
};