009
30.09.2005, 10:15 Uhr
virtual
Sexiest Bit alive (Operator)
|
Also prinzipiell scheint es Dir doch darum zu gehen, etwas einmal in einen stream zu schreiben und es zweimal auszulesen?
Dazu habe ich mal einen Speziellen Stream geschrieben, welchen ich grade überarbeite. Der nchstehende Stream ist noch nicht komplett, funktioniert aber:
C++: |
#ifndef OTEESTREAM_H_INCLUDED_ #define OTEESTREAM_H_INCLUDED_
#include <ostream>
namespace strm {
namespace intern {
template< class Ch, class Tr= std::char_traits<Ch> > class basic_teestreambuf : public std::basic_streambuf< Ch, Tr > {
// === TYPES ====================================================================================== public: /** Type of a stream. */ typedef std::basic_ostream<Ch,Tr> stream_type;
/** Type of a pointer to the stream. */ typedef std::basic_ostream<Ch,Tr>* stream_pointer;
/** Character type. */ typedef Ch char_type;
/** Character pointer type. */ typedef Ch* char_pointer;
/** Character traits type. */ typedef Tr traits_type;
/** Integer type from character traits. */ typedef typename traits_type::int_type int_type;
// === CONSTANTS ================================================================================== private: /** Size of the internal buffer. */ static const std::streamsize BUFFERSIZE = 8192;
// === ATTRIBUTES ================================================================================= private: /** First output streamn or <code>null</code>. */ stream_pointer m_first;
/** Second output streamn or <code>null</code>. */ stream_pointer m_second;
/** Default buffer. */ char_type m_default_buffer[BUFFERSIZE];
// === PRIVATE INTERFCE =========================================================================== private:
/** * Assigment is forbidden. */ basic_teestreambuf& operator = ( const basic_teestreambuf&);
/** * Copy is forbidden. */ basic_teestreambuf( const basic_teestreambuf&);
// === PROTECTED INTERFACE ======================================================================== protected:
/** * Flushes the buffer. * Flushing means that the internal bu´ffer is flushed and then the both attachted streams * as well. * * @return 0 on success. */ virtual int sync() { int_type c; // First write to the subordinated streams if (NULL != pbase() && pbase()!=pptr()) { c = traits_type::to_int_type(overflow()); if (traits_type::eq_int_type(c, traits_type::eof())) { return c; } } // new sync on the subordinated streams if (NULL != m_first) { c = m_first->rdbuf()->pubsync(); if (traits_type::eq_int_type(c, traits_type::eof())) { return c; } } if (NULL != m_second) { c = m_second->rdbuf()->pubsync(); if (traits_type::eq_int_type(c, traits_type::eof())) { return c; } } return 0; }
/** * Sets the buffer. * Initial, this method is called by the constructor in order to set the buffer to an area * of {@link #BUFFERSIZE} bytes. But this method might be also called after construction to * assign a new buffer. By default <code>setbuf(0,0)</code> means "unbuffered" and buffering * is turned off. * <p> * Before changing the buffer, any data in the old buffer is flushed. * * @param buffer Pointer to the new buffer. * @param size Size of the new buffer. * * @return This instance. */ virtual std::basic_streambuf<char_type,traits_type>* setbuf( char_pointer buffer, std::streamsize size) { sync(); // TODO: how to handle an error here? setp(buffer, buffer+size); return this; }
virtual int_type overflow( int_type c = traits_type::eof()) {
char_pointer base = pbase();
// Check, if buffered IO if (NULL!=base) { // This is buffered IO char_pointer ptr = pptr();
// Write to subordinated streams if (NULL!=m_first) { if (m_first->rdbuf()->sputn(base, ptr-base) != ptr-base) { return traits_type::to_int_type(traits_type::eof()); } } if (NULL!=m_second) { if (m_second->rdbuf()->sputn(base, ptr-base) != ptr-base) { return traits_type::to_int_type(traits_type::eof()); } }
// Mark buffer as empty setp(base, epptr());
// If not eof, write to buffer if (!traits_type::eq_int_type(c, traits_type::eof())) { return sputc(c); } } else { // This is unbuffered IO
// Write to subordinated streams int r; if (NULL!=m_first) { r = m_first->rdbuf()->sputc(c); if (traits_type::eq_int_type(r, traits_type::eof())) { return traits_type::to_int_type(traits_type::eof()); } } if (NULL!=m_second) { r = m_second->rdbuf()->sputc(c); if (traits_type::eq_int_type(r, traits_type::eof())) { return traits_type::to_int_type(traits_type::eof()); } } }
return traits_type::to_int_type(c); }
// === PUBLIC INTERFACE =========================================================================== public: /** * Sole constructor. * Creates a new instance with the specified target streams. The buffer is initial set to the * default buffer. * * @param first Pointer to the first stream. * @param second Pointer to the second stream */ basic_teestreambuf(stream_pointer first=NULL, stream_pointer second=NULL) : m_first(first), m_second(second) {
setbuf(m_default_buffer, BUFFERSIZE); }
/** * Destructor. * Ensures that any cached data is flushed. */ virtual ~basic_teestreambuf() { try { sync(); } catch(...) { } }
};
} // namespace intern
template< class Ch, class Tr=std::char_traits<Ch> > class basic_oteestream : public std::basic_ostream< Ch, Tr > {
// === TYPES ====================================================================================== public: /** Type of a stream. */ typedef std::basic_ostream<Ch,Tr> stream_type;
/** Character type. */ typedef Ch char_type;
/** Character traits type. */ typedef Tr traits_type;
typedef intern::basic_teestreambuf<Ch,Tr> teestreambuf_type; public:
basic_oteestream() : stream_type(new teestreambuf_type()) { } basic_oteestream(std::ostream& first) : stream_type(new teestreambuf_type(&first)) { } basic_oteestream(std::ostream& first, std::ostream& second) : stream_type(new teestreambuf_type(&first, &second)) { } };
typedef basic_oteestream<char> oteestream; typedef basic_oteestream<wchar_t> woteestream;
}
#endif
|
Im Prinzip ist ein oteestream - welcher dort ja definiert wird - ein Streamduplizierer. Das folgende Beispiel deomnstriert die ungefähre Arbeitsweise:
C++: |
int main() { std::stringstream ein_stream; strm::oteestream tee(ein_stream, std::cout);
tee<<"Ausgabe nach tee"<<std::endl;
std::string ausgabe = ein_stream.str(); }
|
oteestream erwartet also zwei untergeordnete (bieliebige) Streams als parameter bei der Construction. Alles, was man in den oteestream reintut, kommt zweimal raus, nämlich bei beiden Streams, die man bei der Konstruktion angegeben hat. Im obige Beispiel wird also "Ausgabe nach tee" zum einen zur Standardausgabe ausgegeben, zum anderen auch in den Stringstream ein_stream. Daher kannst Du dann den Inhalt von ein_stream noch in eine Variable kopieren...
Die Klasse ist zwar funktionstüchtig, aber eben noch nicht fertig: ich bin mir noch nicht ganz sicher, ob es sinn macht, oteestream eine Seek-Method zu schenken, ausserdem fehlen noch methoden, die untergeordneten Streams im nachhinein zu verändern. -- Gruß, virtual Quote of the Month Ich eß' nur was ein Gesicht hat (Creme 21) |