Временная проблема возврата Ostream - PullRequest
6 голосов
/ 09 октября 2009

Я создаю регистратор со следующими разделами:

// #define LOG(x) // for release mode
#define LOG(x) log(x)

log(const string& str);
log(const ostream& str);

С идеей сделать:

LOG("Test");
LOG(string("Testing") + " 123");
stringstream s;
LOG(s << "Testing" << 1 << "two" << 3);

Это все работает как задумано, но когда я делаю:

LOG(stringstream() << "Testing" << 1 << "two" << 3);

Не работает:

void log(const ostream& os)
{
  std::streambuf* buf = os.rdbuf();
  if( buf && typeid(*buf) == typeid(std::stringbuf) )
  {
    const std::string& format = dynamic_cast<std::stringbuf&>(*buf).str();
    cout << format << endl;
  }
}

приводит к «формату», содержащему нежелательные данные вместо обычной правильной строки.

Я думаю, это потому, что временный ostream, возвращаемый оператором <<, переживает поток строк, из которого он исходит. </p>

Или я не прав?

(Почему string () работает таким образом? Это потому, что он возвращает ссылку на себя? Я предполагаю, что да.)

Мне бы очень хотелось сделать это так, как я бы исключил дополнительное выделение при входе в режим релиза.

Приветствуются любые указатели или уловки, чтобы сделать это таким образом. В моем реальном решении у меня много разных функций журнала, и все они более сложные, чем эта. Поэтому я бы предпочел, чтобы это было реализовано как-то в вызывающем коде. (И не изменяя мой #define, если это возможно)

Просто чтобы дать идею, пример одного из моих фактических #defines:

#define LOG_DEBUG_MSG(format, ...) \
  LogMessage(DEBUG_TYPE, const char* filepos, sizeof( __QUOTE__( @__VA_ARGS__ )), \
  format, __VA_ARGS__)

, который соответствует функциям журнала varargs printf-подобного, принимающим char *, string () и ostream (), а также функциям не vararg, принимающим string (), exception () и HRESULT.

Ответы [ 2 ]

7 голосов
/ 09 октября 2009

Я думаю Я вижу, что происходит. Это дает ожидаемый результат:

log(std::stringstream() << 1 << "hello");

пока это не так:

log(std::stringstream() << "hello" << 1);

(записывается шестнадцатеричное число, за которым следует цифра "1")

Несколько элементов для объяснения:

  • Значение r не может быть привязано к неконстантной ссылке
  • Можно временно вызывать функции-члены на временном
  • std :: ostream имеет оператор члена << (void *) </li>
  • std :: ostream имеет оператор члена << (int) </li>
  • Для char * оператор не является членом, это оператор << (std :: ostream &, const char *) </li>

В приведенном выше коде std :: stringstream () создает временное значение (значение). Его время жизни не является проблематичным, так как оно должно длиться для всего полного выражения, в которое оно объявлено (то есть до тех пор, пока не будет возвращен вызов log ()).

В первом примере все работает нормально, потому что сначала вызывается оператор-член << (int), а затем возвращаемая ссылка может быть передана оператору << (ostream &, const char *) </p>

Во втором примере оператор << (не может быть вызван с "std :: stringstream ()" в качестве 1-го аргумента, поскольку для этого потребуется привязка к неконстантной ссылке. Однако оператор-член < <(void *) в порядке, так как он является участником. </p>

Кстати: почему бы не определить функцию log () как:

void log(const std::ostream& os)
{
    std::cout << os.rdbuf() << std::endl;
}
6 голосов
/ 09 октября 2009

Измените свой макрос LOG() на это:

#define LOG(x) do { std::stringstream s; s << x; log(s.str()); } while(0)

Это позволит вам использовать следующий синтаксис в журналах отладки, поэтому вам не придется вручную создавать поток строк.

LOG("Testing" << 1 << "two" << 3);

Затем определите его как пустое для выпуска, и у вас не будет дополнительных выделений.

...