Печать строки во временный объект потока в C ++ - PullRequest
4 голосов
/ 04 ноября 2011

У меня есть специальный тип ostringstream, в который я пытаюсь вывести текст как временный объект, но у меня возникли некоторые проблемы. Чтобы было ясно, это по сути то, что я хочу сделать:

ostringstream() << "PARTY DOWN!" << endl;

Теперь, прежде чем вы скажете: «Но Зак, этот код абсолютно бесполезен! Объект уничтожен в конце строки, как бы вы узнали, что он что-то сделал?», Выслушайте меня. Я не пытаюсь сделать это с простыми потоками ostringstream, а скорее с производным классом, в котором деструктор фактически предоставляет путь для выхода данных из объекта. Так что на самом деле это выглядит примерно так:

specialstringstream() << "PARTY DOWN!" << endl;

Если в specialstringstream есть деструктор, который выводит текст в другом месте.

Я не буду вдаваться в подробности того, почему я это делаю. Вы должны поверить мне, что это имеет смысл для того, что мне нужно сделать, и это прекрасно вписывается в существующую гигантскую кодовую базу.

Вот проблема: когда я делаю это, все компилируется и запускается, но вместо вывода «PARTY DOWN!» На вывод выводится адрес указателя. строка. Я определил, что это происходит, потому что оператор, выбранный компилятором для выполнения вывода потока, ostream& operator<< (const void* val), а не ostream& operator<< (ostream& out, const char* s ).

У меня есть смутное представление о том, почему, но я не знаю, как обойти это. Что я могу сделать, чтобы символы * печатались во временный экземпляр потока строк?

Вот краткая версия объекта SpecialStringStream, который демонстрирует поведение:

class SpecialStringStream : public ostringstream
{
  public:
    SpecialStringStream(ostream* regularStream)
    {
      regularStream_ = regularStream;
    }

    ~SpecialStringStream()
    {
      if (regularStream_ != NULL)
        (*regularStream_) << str(); 
    }

  private:
    ostream* regularStream_;
};

Когда я делаю что-то вроде: SpecialStringStream(someStreamPtr) << "PARTY DOWN!" << endl;, я получаю адрес указателя типа "00444D60" в своем выводе вместо сообщения.

РЕДАКТИРОВАТЬ: так как я слишком новый пользователь, чтобы ответить на свой вопрос, вот что я остановился благодаря всем ответам.

Я придумал следующее решение, которое работает под Visual C ++ 8 и со всеми остальными компиляторами, которые мне были нужны. Я создал шаблонный оператор, который в основном лишает const SpecialStringStream своей константности, преобразует его в ostream и позволяет операторам ostream делать свое дело. Не стесняйтесь рвать это в клочья в комментариях и предупреждать меня обо всех ужасных потенциальных ошибках, которые я внес!

template <class T>
std::ostream& operator<<(const SpecialStringStream &o, T msg)
{
    return static_cast<std::ostream&>(const_cast<SpecialStringStream&>(o)) << msg;
}

Ответы [ 4 ]

4 голосов
/ 04 ноября 2011

Перегрузка ostream& operator<< (ostream& out, const char*) нежизнеспособна, поскольку ваш временный не будет привязываться к неконстантной ссылке ostream&.На самом деле вы ничего не можете с этим поделать (поскольку вы не можете привести значение к значению l), кроме объявления локальной переменной и использования этого:

{
  specialstringstream ss;
  ss << "Hello world" << std::endl; // OK, can bind to lvalue
}

Возможное решение: Вы можете объявить другую перегрузку, которая принимает ссылку на rvalue:

std::ostream & operator<<(specialstringstream && o, const char * s)
{
  return o << s; // note: *not* "std::move(o)"
}
2 голосов
/ 20 декабря 2011

Может быть, есть какой-то лучший способ, но я нашел другое решение:

#include <iostream>
#include <sstream>

class LogClass
{ 
    template <typename T>
    friend const LogClass& operator << (const LogClass& lc, const T& v);
public:
    LogClass()
        : str(new std::ostringstream())
        , refCount(new int(1))
    {}

    LogClass(const LogClass& other)
        : str(other.str)
    {
        ++(*refCount);
    }

    ~LogClass()
    {
        --(*refCount);
        if (!*refCount)
        {
            delete refCount;
            std::cout << str->str() << std::endl;
            delete str;
        }
    }
private:
    mutable std::ostringstream *str;
    int *refCount;

    LogClass& operator = (const LogClass&);
};

template <typename T>
const LogClass& operator << (const LogClass& lc, const T& v)
{
    (*lc.str) << v;
    return lc;
}

int main(int , char**)
{
    for (size_t i = 0; i < 10 ; ++i)
    {
        LogClass() << "PARTY DOWN! " << i;
    }
}

Запуск его с помощью Valgrind:

valgrind --tool = memcheck --leak-check = full ./LogClass

== 16197 == Memcheck, детектор ошибок памяти

== 16197 == Copyright (C) 2002-2010, и GNU GPL'd, Джулиан Сьюард и др.

== 16197 == Использование Valgrind-3.7.0.SVN и LibVEX; повторите с ключом -h для получения информации об авторских правах

== 16197 == Команда: ./LogClass

== 16197 ==

ПАРТИЯ ВНИЗ! 0

ПАРТИЯ ВНИЗ! 1

PARTY DOWN! 2

PARTY DOWN! 3

PARTY DOWN! 4

PARTY DOWN! 5

PARTY DOWN! 6

PARTY DOWN! 7

PARTY DOWN! 8

PARTY DOWN! 9

== 16197 ==

== 16197 == РЕЗЮМЕ КАРТЫ:

== 16197 == используется на выходе: 0 байтов в 0 блоках

== 16197 == общее использование кучи: 40 выделений, 40 освобождений, выделение 7350 байт

== 16197 ==

== 16197 == Все блоки кучи были освобождены - утечки невозможны

== 16197 ==

== 16197 == Для подсчета обнаруженных и подавленных ошибок повторно запустите: -v

== 16197 == РЕЗЮМЕ ОШИБОК: 0 ошибок из 0 контекстов (исключено: 15 из 8)

Это то, что я хотел, но оно не безопасно для потоков . Используйте shared_ptr из boost, чтобы сделать это так.

2 голосов
/ 04 ноября 2011

Вы не хотите реализовывать stringstream.Вы хотите реализовать basic_streambuf, который записывает в вашу специальную строку.

Сами потоки отвечают за форматирование и подобные функции;streambufs отвечают за то, что в конечном итоге становится приемником данных.

В конце концов, весь поток строк - это iostream с присоединенным basic_stringbuf. * ​​1005 *

0 голосов
/ 17 июня 2013

вот обходной путь, который я использую:

#define STRM2STR(x) (dynamic_cast<std::ostringstream &>(std::ostringstream() << std::dec << x).str())

вставка std :: dec приведет к вызову ostream :: operator << (ios_base & (* pf) (ios_base &)), который возвращает используемый ostream & </p>

...