Ответ 1
Это:
#define myerr(e) {CiriticalSectionLocker crit; std::cerr << e << std::endl;}
работает на большинстве компиляторов для общего случая myerr("ERR: " << message << number)
.
Самый простой способ создать собственный std::cerr
так, чтобы он был поточно-потоковым.
Я предпочитаю искать код для этого.
Мне нужно, чтобы a line of output
(завершенный с std:: endl), сгенерированный одним потоком, оставался as a line of output
, когда я действительно видел его на моей консоли. (И не смешивается с каким-то другим выходом)
РЕШЕНИЕ: std::cerr
МНОГО медленнее, чем cstdio. Я предпочитаю использовать fprintf(stderr, "The message")
внутри конструктора класса CriticalSectionLocker
whos ', который защищает потоки и дешифрует его.
Это:
#define myerr(e) {CiriticalSectionLocker crit; std::cerr << e << std::endl;}
работает на большинстве компиляторов для общего случая myerr("ERR: " << message << number)
.
Нет. Нет ничего о std::cerr
(или любом другом общедоступном объекте на всем языке), который может знать, какой поток выполняет конкретный вызов << operator
, чтобы предотвратить чередование.
Самое лучшее, что вы можете сделать, это создать кучу объектов журнала, зависящих от потока, которые накапливают данные до достижения новой строки, а затем получить блокировку, которая защищает cerr
, чтобы сразу написать целую строку до cerr
удерживая замок.
Ниже приведено решение, основанное на потокобезопасном лининге, которое я приготовил в какой-то момент. Он использует повышающий мьютекс для обеспечения безопасности потоков. Это немного сложнее, чем необходимо, потому что вы можете подключать выходные политики (если они идут в файл, 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
строго необязательна, это то, что я использую, когда я хочу отключить ведение журнала.
Почему бы просто не создать класс блокировки и использовать его там, где вы хотите сделать потокобезопасный ввод-вывод?
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;
}
Улучшение (которое не подходит для комментария) для подхода в комментарии unixman.
#define LOCKED_ERR \
if(ErrCriticalSectionLocker crit = ErrCriticalSectionLocker()); \
else std::cerr
Что можно использовать как
LOCKED_ERR << "ERR: " << message << endl;
если ErrCriticalSectionLocker реализован тщательно.
Но я лично предпочел бы предложение Кена.