Il y a plusieurs problèmes avec votre solution. Le premier est que vous transmettez stringstream
par valeur, et il ne supporte pas la copie. Vous avez besoin de par référence. La seconde est qu'à l'emplacement de l'appel, la valeur de retour de l'adresse operator<<
surcharges est ostream&
pas stringstream
et puisque stringstream
n'est pas une classe de base de ostream&
(c'est l'inverse l'inverse), vous ne pouvez pas initialiser l'objet stringstream
(ou le stringstream&
) avec elle. Et enfin, il n'y a pas de operator<<
qui prend un stringstream
en tant que paramètre de droite, de sorte que l'instruction dans la section LogStream
ne peut pas fonctionner. Enfin, cela va être quelque peu gênante pour l'utilisateur. Un journal de operator<<
sont des non-membres, avec un ostream&
en tant que premier argument, vous ne pouvez donc pas les appeler avec un temporaire comme argument de gauche. (Dans votre exemple d'appel, bien sûr, vous avez oublié de créer le std::ostringstream
de toute façon ; il ne compilera pas parce qu'il n'y a pas de surcharge de l'option <<
qui prend un char const[]
ou un char const*
comme son opérande de gauche).
Il existe des solutions de contournement pour la plupart de ces problèmes. Quelque chose comme :
void LogStream( std::ostream& text )
{
std::ostringstream& s = dynamic_cast<std::ostringstream&>(text);
m_FileHandle << s.str() << std::endl;
}
gère tous les problèmes sauf le dernier ; ce dernier doit être traité par le client, quelque chose comme :
m_LogObject->LogStream( std::ostringstream().flush() << "..." << x );
(L'appel à std::ostream::flush()
renvoie une référence non-constante à au flux, qui peut être utilisé pour initialiser d'autres flux. std::ostream&
. Et bien que vous ne puissiez pas initialiser une référence non-const avec un temporaire, vous pouvez appeler une fonction membre non-const sur elle).
La maladresse de cette solution pour le code client m'incite à préférer une solution plus complexe. solution plus complexe. Je définis un fichier spécial LogStreamer
classe, quelque chose comme :
class LogStreamer
{
boost::shared_ptr< std::ostream > m_collector;
std::ostream* m_dest;
public:
LogStreamer( std::ostream& dest )
, m_collector( new std::ostringstream )
, m_dest( &dest )
{
}
~LogStreamer()
{
if ( m_collector.unique() ) {
*m_dest << m_collector->str() << std::endl;
}
}
template <typename T>
LogStreamer& operator<<( T const& value )
{
*m_collector << value;
return *this;
}
};
et
LogStreamer LogStream() { return LogStreamer( m_FileHandle ); }
Le code client peut alors écrire :
m_LogObject->LogStream() << "..." << x;
Dans mon propre code : l'objet log est toujours un singleton, l'appel est à travers une macro, qui passe __FILE__
y __LINE__
à la LogStream()
et la cible finale ostream est un streambuf spécial avec une fonction fonction spéciale, appelée par LogStream()
qui prend un nom de fichier et un numéro de ligne, les affiche, ainsi que l'horodatage, au début de la la ligne suivante, et indente toutes les autres lignes. Un filtrage streambuf avec quelque chose comme :
class LogFilter : public std::streambuf
{
std::streambuf* m_finalDest;
std::string m_currentHeader;
bool m_isAtStartOfLine;
protected:
virtual int overflow( int ch )
{
if ( m_isAtStartOfLine ) {
m_finalDest->sputn( m_currentHeader.data(), m_currentHeader.size() );
m_currentHeader = " ";
}
m_isAtStartOfLine = (ch == '\n');
return m_finalDest->sputc( ch );
}
virtual int sync()
{
return m_finalDest->sync();
}
public:
LogFilter( std::streambuf* dest )
: m_finalDest( dest )
, m_currentHeader( "" )
, m_isAtStartOfLine( true )
{
}
void startEntry( char const* filename, int lineNumber )
{
std::ostringstream header;
header << now() << ": " << filename << " (" << lineNumber << "): ";
m_currentHeader = header.str();
}
};
(La fonction now()
renvoie, bien sûr, un std::string
avec le horodatage. Ou un struct tm
et vous avez écrit un <<
para tm
.)