2013-07-21 1 views
6

Я создал свой собственный объект std::cout, который записывает как std::cout, так и в файл журнала.Правильный способ объявления/определения настраиваемого объекта cout-типа

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

Заголовочный файл <MyLib/Log.h>

static LOut { }; 
static LOut lo; 

template<typename T> inline LOut& operator<<(LOut& mLOut, const T& mValue) 
{ 
    std::string str{toStr(mValue)}; 
    std::cout << str; 
    getLogStream() << str; 
    return mLOut; 
} 

Использование:

#include <MyLib/Log.h> 
... 
lo << "hello!" << std::endl; 

Если lo быть static? Должно ли lo быть extern?

Kudos для объяснения правильного способа объявления объекта cout и отображения того, как это делают основные стандартные реализации библиотеки.


Edit: от cout -как объекта, я имею в виду глобальную переменную, которая всегда доступна после включения соответствующего заголовка.

+0

'станд :: cout' обычно просто' станд :: ostream' какой-то, с какой-то особый логику, чтобы обеспечить ее инициализацию достаточно рано и никогда не разрушаться; по крайней мере, один компилятор, который я знаю, использует специальные расширения для достижения этого. Но вам это обычно не нужно; если хуже, то вы можете использовать синглтон и написать 'log() << ...'. –

ответ

5

std::cout просто объявляется следующим образом:

namespace std { 
    extern ostream cout; 
} 

Это регулярная глобальная переменная; вы можете сделать то же самое сами. Поместите объявление extern вашей переменной в заголовок; затем определить ту же переменную в исходном файле и связать его с приложением:

// mylog.h 
extern MyLog mylog; 

// mylog.cpp 
MyLog mylog(someparams); 
+0

Спасибо. Я попытался объявить его «внешним», но забыл определить переменную в исходном файле. –

1

Во-первых, я не слишком уверен, что вы имеете в виду объект cout-like? Возможно, std::ostream.

В любом случае, обычный способ сделать это - использовать фильтрацию streambuf. Просто написать streambuf, который направляет в лог-файл, в дополнение к обычному месту, и вставить его, где бы вы желаете:

class LoggingOutputStreambuf : public std::streambuf 
{ 
    std::streambuf* myDest; 
    std::ofstreambuf myLogFile; 
    std::ostream* myOwner; 
protected: 
    int overflow(int ch) 
    { 
     myLogFile.sputc(ch); // ignores errors... 
     return myDest->sputc(ch); 
    } 
public: 
    LoggingOutputStreambuf(
      std::streambuf* dest, 
      std::string const& logfileName) 
     : myDest(dest) 
     , myLogFile(logfileName.c_str(), std::ios_base::out) 
     , myOwner(nullptr) 
    { 
     if (!myLogFile.is_open()) { 
      // Some error handling... 
     } 
    } 
    LoggingOutputStreambuf(
      std::ostream& dest, 
      std::string const& logfileName) 
     : LoggingOutputStreambuf(dest.rdbuf(), logfileName) 
    { 
     dest.rdbuf(this); 
     myOwner = &dest; 
    } 
    ~LoggingOutputStreambuf() 
    { 
     if (myOwner != nullptr) { 
      myOwner->rdbuf(myDest); 
     } 
    } 
}; 

(Это C++ 11, но это не должно быть трудно ., чтобы изменить его для C++ 03)

для использования, вы можете использовать что-то вроде:

LoggingOutputStreambuf logger(std::cout); 
// ... 

Все выходные в std::cout не будет зарегистрирован до logger выходит объема.

На практике, вы, вероятно, использовать что-то более сложное, чем filebuf для лесозаготовок, так как вы можете вставить метки времени в начале каждой строки, или систематически вровень в конце каждой строки. (Фильтрация streambufs может позаботиться о тех вопросах , а также.)

+0

Спасибо, но мне нужна глобальная переменная, которая всегда доступна, поэтому я говорил о объекте 'cout' –

+0

Итак, вы были обеспокоены временем жизни. Это было непонятно. В этом случае очевидным решением является 'std :: ostream & logStream() {static std :: ostream * theOneAndOnly = new MyStreamType; return * theOneAndOnly; } 'В синтаксисе есть небольшое изменение: вам нужно написать' logStream() << ... ', а не' logStream << ... ', но это все. (И вы все равно можете использовать 'LoggingOutputStreambuf' выше в потоке, который вы возвращаете из' logStream', он остается самым простым и изящным способом вывода вывода в два отдельных адресата.) –

0

В одном из моих projects, я написал обертку для std::cout.

Это выглядит примерно так:

struct out_t { 
    template<typename T> 
    out_t& 
    operator << (T&& x) { 
      std::cout << x; 
      // log << x; 
      return *this; 
    }; 
}; 

out_t out; 

out << 1; 

Для полного кода ищет struct out в io.h

+0

Это ближе к тому, что я хочу - однако я нужен глобальный экземпляр out, который можно использовать без оператора(), например 'cout'. –

+0

Нет ничего, что позволяет вам объявить глобальный объект 'out'. Я редактировал код, чтобы показать, как это сделать. –

+0

@LeonidVolnitsky Если я не использую C++ 11, можно ли заменить '&&' на '&' в отображаемом фрагменте кода? – synaptik

1

станд :: соиЬ-подобный объект, который пишет как для StD :: COUT и в журнал файл

Может быть, boost.iostreams будет достаточно?

#include <iostream> 
#include <fstream> 
#include <boost/iostreams/stream.hpp> 
#include <boost/iostreams/tee.hpp> 

namespace io = boost::iostreams; 
int main() 
{ 
    typedef io::tee_device<std::ostream, std::ofstream> teedev; 
    typedef io::stream<teedev> LOut; 
    std::ofstream outfile("test.txt"); 
    teedev logtee(std::cout, outfile); 
    LOut mLOut(logtee); 
    mLOut << "hello!" << std::endl; 
} 
+0

Я не хочу вводить какую-либо зависимость от boost, и я хочу глобальную переменную типа 'cout' –

1

Просто отправки входного значения прямо в соиЬ не работает для меня, потому что я хотел бы добавить заголовки и данные в журнал вывод.

Кроме того, у меня был статический класс Debug, в который можно было обернуть поток журнала.

Так я сумел это сделать, надеюсь, это полезно. Я как-то newbye на C++, так что не стесняйтесь сказать мне, если что-то не так :)

#include <iostream> 
#include <sstream> 
#include <ostream> 

enum class DebugLevel { 
    INFO, 
    WARNING, 
    ERROR 
}; 

class Debug { 

    public: 

     /* other Debug class methods/properties 
      ... 
     */ 

     // out stream object 
     static struct OutStream { 

       std::ostringstream stream; 
       DebugLevel level = DebugLevel::INFO; 

      public: 

       // here you can add parameters to the object, every line log 
       OutStream& operator()(DebugLevel l) { 
        level = l; 
        return *this; 
       } 

       // this overload receive the single values to append via << 
       template<typename T> 
       OutStream& operator<<(T&& value) { 
        stream << value; 
        return *this; 
       } 

       // this overload intercept std::endl, to flush the stream and send all to std::cout 
       OutStream& operator<<(std::ostream& (*os)(std::ostream&)) { 

        // here you can build the real console log line, add colors and infos, or even write out to a log file 
        std::cout << __TIME__ << " [" << (int)level << "] " << stream.str() << os; 

        stream.str(""); // reset the string stream 
        level = DebugLevel::INFO; // reset the level to info 
        return *this; 
       } 

     } Log; 

}; 

Debug::OutStream Debug::Log; // need to be instantiaded only because I use a static Debug class 

int main() { 

    Debug::Log(DebugLevel::ERROR) << "Hello Log! " << 2 << " " << __FUNCTION__ << std::endl; 

    Debug::Log << "Hello Log! " << 0xFA << std::endl; // NB: this way the debugLevel is default 

    return 0; 

}