2010-08-15 13 views
6

Solía ​​ser un experto en C++ hace una década, pero durante los últimos 10 años he estado programando Java. Acabo de iniciar un proyecto de C++ que utiliza un pequeño analizador XML de terceros. El analizador XML acepta un archivo istream STL. Mis datos XML provienen de un Windows COM IStream. Pensé que haría lo correcto y crearía un adaptador para tomar los datos IStream y presentarlos al analizador XML a través de un istream.¿Cómo implemento seekg() para un istream/streambuf personalizado?

Seguí el excelente tutorial en http://www.mr-edd.co.uk/blog/beginners_guide_streambuf y creé un COMStreambuf que toma datos del COM IStream subyacente y los usé como un buffer para un COMIstream personalizado. Todo se ve bien, pero obtengo un error de lectura del analizador.

Resulta que el analizador lee todo el archivo en la memoria utilizando seekg() en istream para conocer su tamaño y luego vuelve al principio con seekg() para leerlo de una vez. Como era de esperar, el tutorial mencionado anteriormente decidió "guardar [instrucciones para implementar la búsqueda] para otra publicación" que aparentemente nunca se escribió.

¿Puede alguien decirme lo que tengo que hacer para poner en práctica seekg() con mi istream encargo/streambuf? Me aventuraría a hacerlo yo mismo (mi primera inclinación sería anular cosas en istream), pero con mi inexperiencia tan profundo en el STL y con mi mentalidad Java, me temo que haría algo incompleto y tendría una solución frágil. (Sin tutoriales de lectura, por ejemplo, nunca habría adivinado que se crea una istream personalizada escribiendo un streambuf nuevo, por ejemplo, o que necesitaría llamar a imbue() con una configuración regional predeterminada, etc.)

Gracias por cualquier ayuda. Me ha impresionado mucho este sitio, tanto por el conocimiento de los participantes como por su naturaleza amable y honesta al conceder quién tiene la mejor respuesta. :)

Respuesta

3

Supongo que por "seekg" te refieres a seekoff y seekpos.

La forma más sencilla de implementar miembros seekoff y seekpos de su COMStreambuf es envolver el método de la interfaz IStreamSeek. Por ejemplo, algo como esto debería funcionar:

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

En este establecimiento, después de ajustar la posición de streamIn que llamo:

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

Después de una búsqueda, o sputbackcsungetc operará en datos antiguos. Es posible que desee considerar si esto tiene sentido para su aplicación y hacer algo diferente.

+0

¿Qué es 'gback()'? ¿Querías decir 'eback()'? – 0x499602D2

+0

@ 0x499602D2: Sí. Gracias por descubrir esto –

Cuestiones relacionadas