std :: stringstream в качестве параметра - PullRequest
3 голосов
/ 08 июня 2011

Я немного новичок в языке C ++.Я пишу служебный класс для входа в файл.Он работает прекрасно, за исключением того, что теперь я хотел бы улучшить его, сделав его более удобным в использовании (например, передавая потоки строк в функцию журнала).

Это то, что я пытался, и это не сработало.

определение:

void LogStream( std::stringstream i_Log ){ m_FileHandle << i_Log << std::endl; }

вызов:

m_LogObject->LogStream( "MKLBSearchEngine::Search( " << x << ", " << i_Filter << " ) - No Results Found" );

Ответы [ 6 ]

11 голосов
/ 08 июня 2011

Есть несколько проблем с вашим решением. Во-первых, ты передача stringstream по значению, и он не поддерживает копирование. Тебе нужно по ссылке. Во-вторых, на сайте вызова возвращаемое значение operator<< перегрузки ostream&, а не stringstream, и с stringstream не является базовым классом ostream& (это другой способ раунд), вы не можете инициализировать stringstream (или stringstream&) с этим. И, наконец, нет operator<<, который занимает stringstream как параметр правой руки, поэтому оператор в LogStream функция не может работать. Наконец, это будет несколько в любом случае неудобно для пользователя. Журнал operator<< не является членом, с ostream& неконстантной ссылкой в ​​качестве первого аргумента, поэтому вы не можете вызвать их с временным в качестве левого аргумента. (В вашем примере звоните, конечно, вы забыли создать std::ostringstream в любом случае; Это не компилируется, потому что нет перегрузки <<, которая принимает char const[] или char const* в качестве левого операнда.)

Есть обходные пути почти для всех этих проблем. Что-то как:

void LogStream( std::ostream& text )
{
    std::ostringstream& s = dynamic_cast<std::ostringstream&>(text);
    m_FileHandle << s.str() << std::endl;
}

обрабатывает все проблемы, кроме последней; последнее должно быть обработано клиентом, что-то вроде:

m_LogObject->LogStream( std::ostringstream().flush() << "..." << x );

(вызов std::ostream::flush() возвращает неконстантную ссылку на поток, который можно использовать для дальнейшей инициализации std::ostream&. И хотя вы не можете инициализировать неконстантную ссылку с временным, вы можете вызвать неконстантную функцию-член.)

Неловкость этого для клиентского кода заставляет меня вообще предпочитать более сложное решение. Я определяю специальный LogStreamer класс, что-то вроде:

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

и

LogStreamer LogStream() { return LogStreamer( m_FileHandle ); }

Код клиента может написать:

m_LogObject->LogStream() << "..." << x;

В моем собственном коде: объект журнала всегда одиночный, вызов через макрос, который передает __FILE__ и __LINE__ в LogStream() функция, и конечный целевой поток ostream - это специальный потоковый буфер с специальная функция, вызываемая LogStream(), которая принимает имя файла и номер строки, выводит их вместе с отметкой времени в начале вывод следующей строки и отступ всех остальных строк. Фильтрация streambuf с чем-то вроде:

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

(Функция now(), конечно, возвращает std::string с метка времени. Или struct tm, и вы написали << для tm.)

1 голос
/ 08 июня 2011

Ваш вызов функции не будет работать, так как "MKLBSearchEngine::Search( " имеет тип const char * и не имеет перегрузки для оператора <<. Он также не будет работать с std::string("MKLBSearchEngine::Search( "), так как std::string также не имеет такого оператора. Что вы можете сделать, это вызвать его с помощью std::stringstream("MKLBSearchEngine::Search( "), который преобразует первый аргумент в поток, так что следующие операторы работают с этим потоком. Но, как отмечали другие, вам придется сделать аргумент функции константной ссылкой, поскольку потоки не могут быть скопированы (даже тогда это будет довольно неэффективно). Также просто запись std::stringstream в файл не будет делать то, что вы хотите (если он все равно работает), вместо этого вы должны взять его содержимое (базовый std::string). Таким образом, в целом ваш код должен выглядеть так:

void LogStream( const std::stringstream &i_Log ){ m_FileHandle << i_Log.str() << std::endl; }
...
m_LogObject->LogStream( std::stringstream("MKLBSearchEngine::Search( ") << x << ", " << i_Filter << " ) - No Results Found" );

Но вы также можете просто использовать LogString(const std::string &) и позволить пользователю этой функции вызвать stream.str() сам.

1 голос
/ 08 июня 2011

Ваш звонок должен выглядеть как

m_LogObject->LogStream( stringstream() << "MKLBSearchEngine::Search( " << x
 << ", " << i_Filter << " ) - No Results Found" );

, поскольку вам нужно создать объект stringstream, который вы будете передавать в функцию.

Этот вызов подразумевает, что у вас уже есть желаемый выходной поток, поэтому я также рекомендовал бы изменить дизайн вашего класса, чтобы использовать operator<< для ведения журнала, если он уже не перегружен.

1 голос
/ 08 июня 2011

У вас проблема с вашим дизайном.Вы не хотите принимать поток в качестве параметра, либо принимать строку, либо заставлять свой класс вести себя как поток (или оба).

Если вы заставляете ваш объект вести себя как поток, тогда вы делаетеследующее:

m_LogObject << "what to log" << etc;

Для этого просто переопределите оператор <<.

0 голосов
/ 08 июня 2011

Вы передаете std::stringstream экземпляр по значению.Вы хотите избежать копирования и передавать его по ссылке (или указателю).Например:

void LogStream ( std::stringstream & i_Log ){ m_FileHandle << i_Log << std::endl; }

Подробнее о C ++ ссылки .

0 голосов
/ 08 июня 2011

Вы не можете передавать потоковые объекты по значению (поскольку они не копируются), поэтому вам нужно передавать (и хранить) ссылки:

void LogStream(std::stringstream& i_Log){
    m_FileHandle << i_Log << std::endl;
}

Это, вероятно, не будет делать то, что вы 'хотя и ожидаем (вероятно, он напечатает адрес i_Log по довольно непонятным причинам).

Если вы намереваетесь забрать вещи из потока строк, это может сделать то, что вы хотите:

i_Log.get( *m_FileHandle.rdbuf() );
...