2010-12-15 4 views
2

Самый простой способ создать мой собственный std::cerr так, чтобы он был поточно-потоковым.Линейный потокобезопасный std :: cerr для C++

Я предпочитаю код для этого.

Что мне нужно, так что a line of output (завершено с std :: endl), сгенерированным одним потоком, остается as a line of output, когда я действительно вижу его на консоли. (И не смешивается с выходом другой другой резьбы)

РЕШЕНИЕ: std::cerr МНОГО медленнее, чем cstdio. Я предпочитаю использовать fprintf(stderr, "The message") внутри конструктора класса «CriticalSectionLocker», который использует безопасный для потолка замок, и дедуктор отпускает его.

+1

Как вы представляете себе создание потоков локального буфера "горбатый" в 'станд :: cerr' уменьшит буферизацию поверх локального буфера потока «снаружи», а затем написание полных строк в 'std :: cerr'? Буфер - это буфер. 'std :: ostringstream' - типичный общий подход для этого. –

+3

Вы случайно ищете библиотеку протоколов с потоками? – yasouser

+1

Недавно я узнал о проекте log4cpp (http://log4cpp.sourceforge.net/). Не уверен, обеспечивает ли он то, что вы ищете!?!? Может быть, стоит проверить это. – yasouser

ответ

3

Это:

#define myerr(e) {CiriticalSectionLocker crit; std::cerr << e << std::endl;} 

работает на большинстве компиляторов для общего случая myerr("ERR: " << message << number).

+0

Предполагается, что вы не хотите использовать оператор '<<' для форматирования (например, 'cerr <<" Это сообщение журнала было напечатано «<< ntimes <<» раз. »<< endl;') –

+2

Hrm. Я на самом деле ошибаюсь, потому что я временно забыл, как работают макросы. Все, что вам нужно сделать, чтобы получить форматирование, - это вызов 'lerr (« Это сообщение журнала было напечатано »<< ntimes <<" times. ")', И замена текста вернет его. –

5

У вас нет. Нет ничего о std::cerr (и ни один другой общий объект на всем языке), который может знать, какой поток выполняет конкретный вызов << operator, чтобы предотвратить чередование.

Лучшее, что вы можете сделать, это создать кучу нит конкретных объектов регистратора, что каждый накапливают данные до новой строки не будет достигнуто, то получить блокировку, которая защищает cerr так, чтобы написать целую строку сразу cerr удерживая замок.

+0

Я собирался использовать свой собственный класс. Как написать эти объекты журнала, основанные на iostream, которые вы описываете? это был мой первоначальный вопрос. Я ничего не знаю об iostreams, кроме сообщения cerr << " – unixman83

+0

Что я не так уверен. Я думаю, что есть несколько вариантов: # 1 вы можете написать свой собственный streambuf, который сбрасывает данные в 'cerr' и использует обычный ostream для форматирования данных для него, # 2 Вы можете использовать' std :: stringstream' внутри регистратора каждого потока для накопления данные (строковый поток полностью совместим со всеми примитивами форматирования 'cerr', но разоблачение их - большая работа). Там, где вы получаете блокировку, которая будет скрываться до cerr, будет специфичной для платформы. # 3 вы можете сделать что-то с 'sprintf' и форматированием строк. –

+0

Или получить что-то вроде log4cpp –

2

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

#define LOCKED_ERR \ 
    if(ErrCriticalSectionLocker crit = ErrCriticalSectionLocker()); \ 
    else std::cerr 

который может быть использован как

LOCKED_ERR << "ERR: " << message << endl; 

если ErrCriticalSectionLocker тщательно выполнены.

Но я лично предпочел бы предложение Кена.

+0

+1 для творческого использования жизненного цикла временных рядов. Не могли бы вы обеспечить реализацию «ErrCriticalSectionLocker», потому что семантика очень * нетривиальна? –

+0

Хм. Я вижу; Вы можете использовать этот метод без *** «скобки» ***, но я вижу мало смысла в этом, так как мой C (я исхожу из C фона), стиль программирования обычно вызывает скобки. Кроме того, 'ErrCriticalSectionLocker' запутывает ... – unixman83

4

Вот решение, основанное на потоке, основанное на логике, которое я приготовил в какой-то момент. Он использует повышающий мьютекс для обеспечения безопасности потоков. Это немного сложнее, чем это необходимо, потому что вы можете подключить выходные политики (она должна идти в файл, STDERR, или где-нибудь еще?):

logger.h:

#ifndef LOGGER_20080723_H_ 
#define LOGGER_20080723_H_ 

#include <boost/thread/mutex.hpp> 
#include <iostream> 
#include <cassert> 
#include <sstream> 
#include <ctime> 
#include <ostream> 

namespace logger { 
    namespace detail { 

     template<class Ch, class Tr, class A> 
     class no_output { 
     private: 
      struct null_buffer { 
       template<class T> 
       null_buffer &operator<<(const T &) { 
        return *this; 
       } 
      }; 
     public: 
      typedef null_buffer stream_buffer; 

     public: 
      void operator()(const stream_buffer &) { 
      } 
     }; 

     template<class Ch, class Tr, class A> 
     class output_to_clog { 
     public: 
      typedef std::basic_ostringstream<Ch, Tr, A> stream_buffer; 
     public: 
      void operator()(const stream_buffer &s) { 
       static boost::mutex mutex; 
       boost::mutex::scoped_lock lock(mutex); 
       std::clog << now() << ": " << s.str() << std::endl; 
      } 

     private: 
      static std::string now() { 
       char buf[64]; 
       const time_t tm = time(0); 
       strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tm)); 
       return buf; 
      } 

     }; 

     template<template <class Ch, class Tr, class A> class OutputPolicy, class Ch = char, class Tr = std::char_traits<Ch>, class A = std::allocator<Ch> > 
     class logger { 
      typedef OutputPolicy<Ch, Tr, A> output_policy; 
     public: 
      ~logger() { 
       output_policy()(m_SS); 
      } 
     public: 
      template<class T> 
      logger &operator<<(const T &x) { 
       m_SS << x; 
       return *this; 
      } 
     private: 
      typename output_policy::stream_buffer m_SS; 
     }; 
    } 

    class log : public detail::logger<detail::output_to_clog> { 
    }; 
} 

#endif 

Использование выглядит следующим образом:

logger::log() << "this is a test" << 1234 << "testing"; 

сведению отсутствие '\n' и std::endl, так как это подразумевается. Содержимое буферизуется, а затем атомарно выводится с использованием политики, указанной в шаблоне. Эта реализация также добавляет строку с меткой времени, поскольку она предназначена для ведения журнала. Политика no_output строго необязательна, это то, что я использую, когда хочу отключить ведение журнала.

3

Почему бы не просто создать класс блокировки и использовать его там, где вы хотите сделать потокобезопасный ввод-вывод?

class LockIO 
{ 
    static pthread_mutex_t *mutex; 
public: 
    LockIO() { pthread_mutex_lock(mutex); } 
    ~LockIO() { pthread_mutex_unlock(mutex); } 
}; 

static pthread_mutex_t* getMutex() 
{ 
    pthread_mutex_t *mutex = new pthread_mutex_t; 
    pthread_mutex_init(mutex, NULL); 
    return mutex; 
} 
pthread_mutex_t* LockIO::mutex = getMutex(); 

Тогда вы положили любой IO вы хотите в блоке:

std::cout <<"X is " <<x <<std::endl; 

становится:

{ 
    LockIO lock; 
    std::cout <<"X is " <<x <<std::endl; 
}