Строковый потокобезопасный std:: cerr для С++

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

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

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

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

Ответ 1

Это:

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

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

Ответ 2

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

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

Ответ 3

Ниже приведено решение, основанное на потокобезопасном лининге, которое я приготовил в какой-то момент. Он использует повышающий мьютекс для обеспечения безопасности потоков. Это немного сложнее, чем необходимо, потому что вы можете подключать выходные политики (если они идут в файл, 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 строго необязательна, это то, что я использую, когда я хочу отключить ведение журнала.

Ответ 4

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

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

Ответ 5

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

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

Что можно использовать как

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

если ErrCriticalSectionLocker реализован тщательно.

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