2012-02-14 8 views
8

Estoy usando gcc 4.6. Supongamos que hay un vector v de parámetros que tengo que pasar a una función variable f (formato const char *, ...).Pasando std :: vector <int> elementos a la función variadic

Un método de hacer esto es:.

 void VectorToVarArgs(vector<int> &v) 
     { 
      switch(v.size()) 
      { 
       case 1: f("%i",    v[0]); 
       case 2: f("%i %i",   v[0], v[1]); 
       case 3: f("%i %i %i",  v[0], v[1], v[2]); 
       case 4: f("%i %i %i %i", v[0], v[1], v[2], v[3]); 

       // etc... 
       default: 
        break; 
      } 
     } 

     // where function f is 
     void f(const char* format, ...) 
     { 
      va_list args; 
      va_start (args, format); 
      vprintf (format, args); 
      va_end (args); 
     } 

El problema es, por supuesto que no es compatible con un número arbitrario de elementos en el vector v Sin embargo, creo haber entendido cómo va_lists obras en las principio, es decir, mediante la lectura de los argumentos de la pila, a partir de la dirección del último argumento con nombre antes de "...", Ahora pensé que debería ser posible copiar los valores de los elementos del vector de un bloque de memoria (por ejemplo myMemBlock) y pasar su dirección como el segundo argumento después de 'formato'. Obviamente, eso requeriría que myMemBlock esté estructurado como se espera por f(), es decir, como una pila.

  1. ¿Es posible?
  2. Alternativamente, ¿es posible presionar los valores de elementos vectoriales en la pila real con alguna magia de ensamblador en línea, llamar a la función f() y limpiar la pila después?

Por último, cosas que no se preocupan por:

  1. El código no podría ser portátil. OK, solo estoy interesado en gcc.
  2. Puede haber otros enfoques que involucren hackers de preprocesador.
  3. Se desaconseja el uso de la función variadic para formatear como printf() para C++.
  4. Uso de funciones de plantilla variadic.
+1

Por lo que sé, no es posible "forjar" su propia lista va_arg. Lo que podrías hacer es pasar el vector directamente al sobrecargar tu función f(). – Gui13

+0

No quiero reimplementar va_arg, solo quiero imitar una pila para que va_arg funcione. – user1142580

Respuesta

2

Bien, ¡aquí hay una solución parcial! Parcial, porque no se aplica a realmente funciones variadas, sino a aquellas que aceptan una va_list como argumento. Pero creo que la solución completa no está muy lejos.

Se basa en los ejemplos que encontré aquí:

  1. crear dinámicamente un va_list https://bbs.archlinux.org/viewtopic.php?pid=238721

  2. forja un va_list http://confuseddevelopment.blogspot.com/2006/04/dynamically-creating-valist-in-c.html

Este código se ha probado con gcc en linux y VC++ 2008 con éxito, también pueden admitirse otras plataformas, pero eso depende de usted.

La idea importante para mí era que un va_list es básicamente nada más que un conjunto empaquetado, que puede ser llenado con los datos de forma dinámica y se puede pasar a funciones como vprintf, vfprintf, vsprintf el que lo acepta como argumento.

Transmitir elementos vectoriales a una de esas funciones puede funcionar asignando suficiente memoria para los elementos del vector y copiarlos antes de la llamada.

Dicho esto, aquí es el enfoque pila dinámicamente la asignación de:

#include <iostream> 
#include <stdio.h> 
#include <stdarg.h> 
#include <string> 
#include <vector> 
#include <alloca.h> 

using namespace std; 


class Format 
{ 
    typedef vector<unsigned long> ULVector; 
    ULVector _args; 
    string _format; 

    public: 
     Format(const char* format) : _format(format) 
     {} 

     Format &operator<<(int arg) { 
      _args.push_back((unsigned long)arg); 
      return *this; 
     } 

     Format &operator<<(const char* arg) { 
      _args.push_back((unsigned long)arg); 
      return *this; 
     } 

     string format() { 
      union { 
       va_list varargs; 
       unsigned long* packedArray; 
      } fake_va_list; 

      // malloc would do it as well! 
      // but alloca frees the mem after leaving this method 
      unsigned long *p = (unsigned long*)alloca(_args.size() * sizeof(unsigned long)); 
      fake_va_list.packedArray = p; 

      ULVector::iterator i = _args.begin(); 
      for (int n=0; i != _args.end(); i++, n++) { 
       p[n] = *i; 
      } 

      char buffer[512]; 
      const char* fmt = _format.c_str(); 
      vsprintf(buffer, fmt, fake_va_list.varargs); 

      // place a free(p) here if you used malloc 
      return string(buffer); 
     } 
}; 


ostream& operator <<=(ostream &os, Format &obj) { 
     os << obj.format(); 
     return os; 
} 


int main() 
{ 
    // we use '<<=' operator here which has lower precedence than '<<' 
    // otherwise we have to write 
    // cout << (Format("\n%x %s %x %c\n") << etc.); 
    cout <<= Format("\n%x %s %x %c\n") << 0x11223344 << "VectorToVarArg" << 0xAABBCCDD << '!'; 
    return 0; 
} 

Adivina lo que hace? Permite el formato de estilo printf (..) con los parámetros recopilados en un vector. Sí, no es perfecto pero hace lo que yo quería. Además, se refiere a dos plataformas principales: D

Además, tienen vistazo a este artículo: va_pass http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a

+0

Bien, la respuesta completa se puede lograr utilizando el enfoque presentado aquí: http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a Sin embargo, debido a una licencia diferente, no puedo copiar el código aquí simplemente. – user1142580

0

Puede usar el algoritmo STL for_each para imprimir cada elemento del vector.

3

Hay una sección "Creando una lista va_ falsa" en http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html. Es para Cocoa, pero es posible que puedas encontrar algo en la red para GCC.

Entonces, estoy adivinando que haría algo como esto:

#include <string> 
#include <cstdio> 
#include <vector> 
#include <cstdarg> 
using namespace std; 

struct my_list { 
    unsigned int gp_offset; 
    unsigned int fp_offset; 
    void *overflow_arg_area; 
    void *reg_save_area; 
}; 

void f(const char* format, ...) { 
    va_list args; 
    va_start (args, format); 
    vprintf (format, args); 
    va_end (args); 
} 

void test(const vector<int>& v) { 
    string fs; 
    for (auto i = v.cbegin(); i !=v.cend(); ++i) { 
     if (i != v.cbegin()) { 
      fs += ' '; 
     } 
     fs += "%i"; 
    } 
    my_list x[1]; 
    // initialize the element in the list in the proper way 
    // (however you do that for GCC) 
    // where you add the contents of each element in the vector 
    // to the list's memory 
    f(fs.c_str(), x); 
    // Clean up my_list 
} 

int main() { 
    const vector<int> x({1, 2, 3, 4, 5}); 
    test(x); 
} 

Pero, no tengo absolutamente ninguna pista. :)

+0

¡Eso se ve muy interesante! ¡Gracias por ahora! Informaré los resultados. – user1142580

+1

¡Gracias por su útil pista! – user1142580

2

Su reflexión no está en el nivel correcto de abstracción.

Cuando diga que quiere convertir el vector a una lista de argumentos variables, es porque la función que toma la lista de argumentos variables le interesa.

La verdadera pregunta es, por tanto, ¿cómo podría hacer lo mismo que f, pero a partir de vector?

Es posible que reenviar la llamada a f termine por comenzar la solución, pero no es obvio.

si es sólo acerca de la impresión:

void f(std::vector<int> const& vi) { 
    bool first = true; 
    for (int i: vi) { 
    if (first) { first = false; } else { std::cout << ' '; } 
    std::cout << i; 
    } 
} 

O, si tiene acceso a bibliotecas externas:

#include <boost/algorithm/string/join.hpp> 

void f(std::vector<int> const& vi) { 
    std::cout << boost::join(vi, " "); 
} 

en cuyo punto el interés del f en realidad no es evidente por más tiempo.

+0

Digamos que se trata solo de imprimir. En aras de la simplicidad, definí v como vector . Sin embargo, esto debería haber sido solo un punto de partida para mí. En caso de que v se cambie para contener diferentes tipos de POD, me gustaría utilizar el formato vprintf(). – user1142580

1

A juzgar por su propia respuesta dada, parece que se podría hacer uso de boost format.

Ejemplos:

#include <iostream> 
#include <string> 
#include <sstream> 
#include <boost/format.hpp> 
using namespace std; 
using namespace boost; 

template <typename T> 
string formatted_str_from_vec(const T& v) { 
    ostringstream fs; 
    size_t count = 1; 
    for (const auto& i : v) { 
     if (&i != &v[0]) { 
      fs << " "; 
     } 
     fs << '%' << count << '%'; 
     ++count; 
    } 
    format fmtr(fs.str()); 
    for (const auto& i : v) { 
     fmtr % i; 
    } 
    // looks like fmtr("%1% %2% %3% %4%") % v[0] % v[1] etc. 
    return fmtr.str(); 
} 

int main() { 
    cout << formatted_str_from_vec(vector<int>({1, 2, 3, 4, 5, 6, 7, 8, 8, 10, 11, 12})) << endl; 
    cout << formatted_str_from_vec(vector<string>({"a", "b", "c"})) << endl; 
    format test1("%1% %2% %3%"); 
    test1 % 1 % "2" % '3'; 
    cout << test1.str() << endl; 
    format test2("%i %s %c"); 
    test2 % 1 % "2" % '3'; 
    cout << test2.str() << endl; 
    format test3("%1% %2%"); 
    test3.exceptions(io::no_error_bits); 
    test3 % 'g'; 
    cout << test3.str() << endl; 
    format test4("%%1%% = %1%"); 
    test4 % "zipzambam"; 
    cout << test4.str() << endl; 
} 

// g++ -Wall -Wextra printvector.cc -o printvector -O3 -s -std=c++0x 

Por supuesto, nada de eso es necesario simplemente imprimir un vector.

+0

Mencioné que esta era una solución parcial. La solución completa que aborda la pregunta tal como la escribí se puede lograr mediante el uso de va_pass que comencé en mi propia respuesta. Su sugerencia puede ser útil en muchos casos (es decir, en caso de que el impulso esté disponible o sea apreciado), por lo tanto, "vote hacia arriba". – user1142580

Cuestiones relacionadas