2010-08-15 13 views
6

Una volta ero un esperto in C++, ma negli ultimi 10 anni ho programmato Java. Ho appena avviato un progetto C++ che utilizza un parser XML di terze parti. Il parser XML accetta un istream STL. I miei dati XML provengono da un IStream COM di Windows. Ho pensato di fare la cosa giusta e creare un adattatore per prendere i dati di IStream e presentarlo al parser XML attraverso un istream.Come implementare seekg() per un istream/streambuf personalizzato?

Ho seguito l'eccellente esercitazione su http://www.mr-edd.co.uk/blog/beginners_guide_streambuf e creato un COMStreambuf che preleva i dati dal sottostante IStream COM e lo utilizza come buffer per un COMIstream personalizzato. Tutto sembra a posto, ma ricevo un errore di lettura dal parser.

Si scopre che il parser legge l'intero file in memoria utilizzando seekg() sull'istream per trovare le sue dimensioni e quindi torna all'inizio con seekg() per leggerlo in una volta. Non sorprendentemente, il tutorial di cui sopra ha deciso di "salvare [istruzioni per implementare la ricerca] per un altro post" che apparentemente non è mai stato scritto.

Qualcuno può dirmi cosa devo fare per implementare seekg() con il mio istream/streambuf personalizzato? Mi azzarderei a farlo da solo (la mia prima inclinazione sarebbe quella di scavalcare la roba in istream), ma con la mia inesperienza così profonda nel STL e con la mia mentalità di Java temo di fare qualcosa di incompleto e di avere una soluzione fragile. (Senza leggere tutorial, ad esempio, non avrei mai immaginato che si creasse un istream personalizzato scrivendo un nuovo streambuf, o che avrei bisogno di chiamare imbue() con una locale predefinita, ecc.)

Grazie per qualsiasi aiuto. Sono rimasto molto impressionato da questo sito --- sia con la conoscenza dei partecipanti che con la loro natura amichevole e onesta nel concedere chi ha la migliore risposta. :)

risposta

3

Suppongo che per "seekg" intenda seekoff e seekpos.

Il modo più semplice per implementare i membri seekoff e seekpos del vostro COMStreambuf è quello di avvolgere il metodo dell'interfaccia IStreamSeek. Ad esempio, qualcosa come questo dovrebbe funzionare:

// COMStreambuf.cpp 
COMStreambuf::pos_type COMStreambuf::seekoff(COMStreambuf::off_type off_, std::ios_base::seekdir way_, std::ios_base::openmode which_) 
{ 
    union { 
     LARGE_INTEGER liMove; 
     ULARGE_INTEGER uliMove; 
    }; 
    liMove.QuadPart = off_; 

    DWORD dwOrigin = STREAM_SEEK_SET; 
    if (way_ == std::ios_base::cur) { 
     dwOrigin = STREAM_SEEK_CUR; 
    } else if (way_ == std::ios_base::end) { 
     dwOrigin = STREAM_SEEK_END; 
    } else { 
     assert(way_ == std::ios_base::beg); 
     dwOrigin = STREAM_SEEK_SET; 
     uliMove.QuadPart = off_; 
    } 

    ULARGE_INTEGER uliNewPosition; 
    if (which_ & std::ios_base::in) { 
     if (which_ & std::ios_base::out) 
      return pos_type(off_type(-1)); 
     HRESULT hres = streamIn->Seek(liMove, dwOrigin, &uliNewPosition); 
     if (hres != S_OK) 
      return pos_type(off_type(-1)); 

     setg(eback(), egptr(), egptr()); 
    } else if (which_ & std::ios_base::out) { 
     HRESULT hres = streamOut->Seek(liMove, dwOrigin, &uliNewPosition); 
     if (hres != S_OK) 
      return pos_type(off_type(-1)); 

     setp(pbase(), epptr(), epptr()); 
    } else { 
     return pos_type(off_type(-1)); 
    } 

    return pos_type(uliNewPosition.QuadPart); 
} 

COMStreambuf::pos_type COMStreambuf::seekpos(COMStreambuf::pos_type sp_, std::ios_base::openmode which_) 
{ 
    return seekoff(off_type(sp_), std::ios_base::beg, which_); 
} 

In questo profilo, dopo aver impostato la posizione di streamIn io chiamo:

setg(eback(), egptr(), egptr()); 

Dopo un cercare, sputbackc o sungetc opererà su vecchi dati. Potresti considerare se questo ha senso per la tua applicazione e fare qualcosa di diverso.

+0

Che cos'è 'gback()'? Intendevi 'eback()'? – 0x499602D2

+0

@ 0x499602D2: Sì. Grazie per aver individuato questo. –