2010-07-28 26 views
13

Tengo una funciónCrear va_list dinámicamente

void foo(int cnt, va_list ap); 

tengo que usarlo, pero el requisito es bastante estricto, número de va_list variar y que va a cambiar durante el tiempo de ejecución. Lo que me gustaría hacer es:

crear un formulario va_list (que espera char*)

QList<Contact*> 

donde Contact es una clase definida

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

}; 

y me gustaría para poblar en el bucle va_list por ejemplo:

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

} 

¿Alguien tiene una pista sobre cómo podría hacer esto?

+1

¿Se puede alterar la función 'foo'? – adf88

+2

duplicado posible de http://stackoverflow.com/questions/988290/populating-a-va-list – Hasturkun

+0

definitivamente duplicado en términos de la respuesta correcta. – bmargulies

Respuesta

0

Si el número de elementos en la lista es limitado, iría para el despacho manual dependiendo de la cantidad de elementos.

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

Hay una razón para que esta no sea la forma más eficiente de hacerlo es: Puedo tener 1-999 elementos de contacto y esto se vería bastante feo. Esperaba encontrar una solución mejor eficiente. Rgds Lukasz – user404251

+0

No estoy seguro de que sea * mejor-eficiente * para llamar a una función con 999 parámetros. –

+0

¿Estás seguro de que la función foo en la biblioteca podrá manejar una lista_va con 999 parámetros? –

0

Depende de lo que el compilador es el tipo va_list, ¿cuáles son las macros va_start y va_end. No puedes hacer esto de forma estándar. Tendría que usar la construcción específica del compilador.

¿Quizás pueda modificar la función 'foo'? Si es así, hazlo a la inversa: convierte va_list en QList y haz que 'foo' acepte QList.

// EDITAR

luego ver qué tipo va_list es, lo que las macros va_start y va_end están en su compilador específico. Luego construye tu va_list de tal manera que estas macros trabajen en él.

+0

Realmente podría esperar eso. Desarrollo la aplicación QT para Windows IDE: Visual Studio 2005. – user404251

+0

No puedo hacer esto, esto si la función está expuesta por LIB :). No tengo un código fuente para eso. – user404251

+0

No puedo hacer esto, esta es la función expuesta por LIB :). No tengo un código fuente para eso. – user404251

3

Su pregunta está etiquetada C++ y hay maneras agradables (como las de secuencia) para evitar varargs completamente en C++.

Este es un gran ejemplo de por qué va_args puede causar dolor. Si tiene alguna posibilidad de cambiar la firma de foo, esa es su mejor opción. Tomando std::vector<std::string> en lugar de va_list simplemente resolvería su problema allí mismo.

Si foo está en una biblioteca externa que no puede cambiar, mi próxima sugerencia sería encontrar una biblioteca diferente.

Si ninguno de ellos es una opción, parece que debería haber una manera de crear recursivamente la lista de llamadas usando va_list, pero no pude encontrar la manera de hacerlo funcionar.

5

Lo que quieres hacer es simular la pila de llamadas para que puedas pasar una lista va_construida a foo(). Esto es más bien específico del compilador (y advertencia, existen diferencias incluso entre compiladores de 32 y 64 bits). El siguiente código es para ¡¡¡FINES DE ENTRETENIMIENTO SOLAMENTE !!! como (si incluso funciona en su sistema) es propenso a la rotura. Con él, uso un buffer de memoria plano y lo lleno con un conteo y un montón de cadenas de caracteres. Puede llenarlo según corresponda con punteros a sus hilos y entregarlos.

Parece que funciona en mi sistema, Windows 7 con Visual Studio 2008, solo para aplicaciones de 32 bits.

* BAD IDEA CODE FOLLOWS !!! *

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

ahora voy a ir pasar la cara de vergüenza por haber pensado en esta idea.

+0

No solo es una mala idea, posiblemente incluso un estándar conflictivo de C (C99: 7.15.1.1.2, 7.15.1.3.2 y posiblemente 7.15.1.4.2), posiblemente un comportamiento indefinido. Aunque no estoy muy seguro. – Aconcagua

-1

< sólo por diversión >

  • permitiendo argumento arbitrario cuenta
  • suerte sizeof (std :: wstring) es un múltiplo de sizeof (int)
  • probado en w2k3 SP2 de 32 bits + 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); 
} 
 

</sólo por diversión >

3

... hmmm ... tal vez no es portátil ... seguro que no es agradable ... pero puede resolver yor problema ...

  • va_list es (al menos para Visual C++) sólo una #define para char *
  • → argumentos no necesitan estar en la pila
  • → argumentos solo están obligados a ser continuo en la memoria
  • → hay necesidad de usar ensamblador y/o copia (véase mi 'sólo por diversión respuesta ':-)
  • → sin necesidad de preocuparse por la limpieza
  • eficiente!
  • probado en w2k3 SP2 de 32 bits + 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

Lo que está intentando utilizar es alloca. Un objeto va_list no puede almacenar variables, la función lo almacena y solo puede acceder a él a través de va_list. Estas variables solo son válidas durante la llamada y luego se recuperan.

esto no funcionará:

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

para asignar memoria en la pila, sin tener que escribir funciones variadic utilizan alloca. Funciona más o menos como malloc, pero no tiene que llamar al free, se libera automágicamente al salir del alcance.

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

Es fundamentalmente el mismo que escribir

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

Pero con alloca 3 no tiene que ser una constante. De nuevo, solo puede usarlo dentro del alcance adjunto, por lo tanto, no devolverlo desde la función.

si lo que quieres de un va_list es los múltiples tipos en una lista que no hacer una unión como esta:

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