2012-02-14 13 views
8

Sto utilizzando gcc 4.6. Supponiamo che ci sia un vettore v di parametri che devo passare ad una funzione variadica f (formato const char *, ...).Passaggio di articoli std :: vector <int> a variadic

Un approccio di fare questo è:.

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

Il problema è, naturalmente, che non supporta un numero arbitrario di elementi nel vettore v Tuttavia, credo di aver capito come va_lists opere in principio, cioè leggendo gli argomenti dallo stack, iniziando dall'indirizzo dell'ultimo argomento denominato prima di "...", Ora pensavo che fosse possibile copiare i valori degli elementi vettoriali in un blocco di memoria (ad esempio myMemBlock e passa il suo indirizzo come secondo argomento dopo "formato". Ovviamente ciò richiederebbe che myMemBlock sia strutturato come previsto da f(), cioè come una pila.

  1. È una cosa fattibile?
  2. In alternativa, è possibile spostare i valori degli elementi vettoriali nello stack reale con alcune magie assembler inline, , funzione di chiamata f() e pulire lo stack in seguito?

Infine, cose che non si preoccupano:

  1. Il codice potrebbe essere non portabile. OK, sono solo interessato a gcc.
  2. Potrebbero esserci altri approcci che coinvolgono l'hackery del preprocessore.
  3. L'utilizzo della funzione variadic per la formattazione come printf(), è sconsigliato per C++.
  4. Utilizzo delle funzioni del modello variadic.
+1

Da quello che so, non è possibile "forgiare" la propria lista va_arg. Quello che potresti fare è passare il vettore direttamente sovraccaricando la tua funzione f(). – Gui13

+0

Non voglio reimplementare va_arg, voglio solo imitare uno stack per cui va_arg a operare. – user1142580

risposta

2

Ok, ecco una soluzione parziale! Parziale, perché non si applica a in realtà funzioni variadic, ma a quelli che accettano un va_list come argomento. Ma penso che la soluzione completa non sia lontana.

Essa si basa sugli esempi che ho trovato qui:

  1. creare dinamicamente una va_list https://bbs.archlinux.org/viewtopic.php?pid=238721

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

Questo codice è stato testato con gcc su linux e VC++ 2008 con successo, anche altre piattaforme potrebbero essere supportate, ma dipende da voi.

L'intuizione importante per me era che un va_list è sostanzialmente nulla più di un array compresso, che può essere riempito con i dati in modo dinamico e può essere passato a funzioni come vprintf, vfprintf, vsprintf che lo accetta come argomento.

Quindi passare gli elementi vettoriali a una di queste funzioni può funzionare allocando memoria sufficiente per gli elementi vettoriali e copiarli prima della chiamata.

Detto questo, qui è la allocazione dinamicamente approccio pila:

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

Indovinate che cosa fa? Consente la formattazione di stile printf (..) con i parametri raccolti in un vettore. Sì, non è perfetto ma fa quello che volevo. Inoltre, esso copre due principali piattaforme: D

Inoltre, hanno un'occhiata a questo articolo: va_pass http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a

+0

Ok, la risposta completa può essere ottenuta utilizzando l'approccio presentato qui: http://www.codeproject.com/Articles/9968/va_list-va_start-va_pass-or-how-to-pass-variable-a Tuttavia, a causa con una licenza diversa non riesco a copiare il codice qui semplicemente. – user1142580

0

È possibile utilizzare l'algoritmo STL for_each per stampare ogni elemento del vettore.

3

C'è una sezione "Creazione di una falsa va_list" al numero http://cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html. È per Cocoa, ma potresti trovare qualcosa in rete per GCC.

Poi, io sono indovinando si farebbe qualcosa di simile:

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

Ma, non ho assolutamente idea. :)

+0

Sembra molto interessante! Grazie per ora! Riferirò i risultati – user1142580

+1

Grazie per il tuo suggerimento utile! – user1142580

2

La tua riflessione non è al livello giusto dell'astrazione.

Quando si dice di convertire il vettore in un elenco di argomenti variabili, è perché la funzione che assume l'elenco di argomenti variabili ti interessa.

La vera domanda è quindi, come potrei fare la stessa cosa di f, ma a partire da un vector?

È possibile che l'inoltro della chiamata a f potrebbe finire per iniziare la soluzione, ma non è ovvio.

se è solo sulla stampa:

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

Oppure, se si ha accesso a librerie esterne:

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

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

A tal punto l'interesse di f non è davvero evidente più.

+0

Diciamo che si tratta solo di stampa. Per semplicità ho definito v come vettore . Tuttavia questo dovrebbe essere stato solo un punto di partenza per me stesso. Nel caso in cui v venisse modificato per contenere diversi tipi di POD, mi piacerebbe utilizzare la formattazione vprintf(). – user1142580

1

A giudicare da soli la propria risposta data, suona come si potrebbe fare uso di boost format.

Esempi:

#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 

Naturalmente, niente di tutto ciò che è necessario per stampare appena fuori un vettore.

+0

Ho detto che questa era una soluzione parziale. La soluzione completa che affronta la domanda così come l'ho scritta, può essere raggiunta usando va_pass che ho commentato sotto la mia risposta. Il tuo suggerimento potrebbe essere utile in molti casi (ad esempio nel caso in cui l'aumento sia disponibile o apprezzato), quindi "vota". – user1142580

Problemi correlati