настроить Cout - PullRequest
       15

настроить Cout

10 голосов
/ 23 марта 2009

Как я могу получить класс из cout, чтобы, например, записать в него

new_cout << "message";

будет эквивалентно

cout << __FUNCTION__ << "message" << "end of message" << endl;

Ответы [ 6 ]

29 голосов
/ 23 марта 2009
class Log
{
public:
    Log(const std::string &funcName)
    {
        std::cout << funcName << ": ";
    }

    template <class T>
    Log &operator<<(const T &v)
    {
        std::cout << v;
        return *this;
    }

    ~Log()
    {
        std::cout << " [end of message]" << std::endl;
    }
};

#define MAGIC_LOG Log(__FUNCTION__)

Таким образом:

MAGIC_LOG << "here's a message";
MAGIC_LOG << "here's one with a number: " << 5;
2 голосов
/ 23 марта 2009
#define debug_print(message) (std::cout << __FUNCTION__ << (message) << std::endl)

Это имеет то преимущество, что вы можете отключить все сообщения отладки сразу, когда вы закончите

#define debug_print(message) ()
1 голос
/ 23 марта 2009

В дополнение к ответу Николая, в моем коде есть следующая реализация. Использование

         LOG_DEBUG("print 3 " << 3); 

печать

         DEBUG (f.cpp, 101): print 3 3 

Вы можете изменить его, чтобы использовать FUNCTION вдоль / вместо LINE и FILE

/// Implements a simple logging facility. 
class Logger
{
        std::ostringstream os_;
        static Logger* instance_;
        Logger();
public:
        static Logger* getLogger();
        bool isDebugEnabled() const;
        void log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const;
        std::ostringstream& getStream()
        { return os_; }
};

void Logger::log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const
{
        std::cout << logLevelEnumToString(l) << "\t(" << fileName << ": " << lineno << ")\t- " << os.str();
        os.str("");
}

#define LOG_common(level, cptext) do {\
        utility::Logger::getLogger()->getStream() << cptext; \
        utility::Logger::getLogger()->log(utility::level, utility::Logger::getLogger()->getStream(), __FILE__, __LINE__);  \
} while(0); 

enum LogLevelEnum {
        DEBUG_LOG_LEVEL,
        INFO_LOG_LEVEL,
        WARN_LOG_LEVEL,
        ERROR_LOG_LEVEL,
        NOTICE_LOG_LEVEL,
        FATAL_LOG_LEVEL
};

#define LOG_DEBUG(cptext)    LOG_common(DEBUG_LOG_LEVEL, cptext)
#define LOG_INFO(cptext)     LOG_common(INFO_LOG_LEVEL , cptext)
#define LOG_WARN(cptext)     LOG_common(WARN_LOG_LEVEL , cptext)
#define LOG_ERROR(cptext)    LOG_common(ERROR_LOG_LEVEL, cptext)
#define LOG_NOTICE(cptext)   LOG_common(NOTICE_LOG_LEVEL, cptext)
#define LOG_FATAL(cptext)    LOG_common(FATAL_LOG_LEVEL, cptext)

const char* logLevelEnumToString(LogLevelEnum m)
{
        switch(m)
        {
                case DEBUG_LOG_LEVEL:
                        return "DEBUG";
                case INFO_LOG_LEVEL:
                        return "INFO";
                case WARN_LOG_LEVEL:
                        return "WARN";
                case NOTICE_LOG_LEVEL:
                        return "NOTICE";
                case ERROR_LOG_LEVEL:
                        return "ERROR";
                case FATAL_LOG_LEVEL:
                        return "FATAL";
                default:
                        CP_MSG_ASSERT(false, CP_TEXT("invalid value of LogLevelEnum"));
                        return 0;
        }
}
0 голосов
/ 23 марта 2009

В целях регистрации я использую что-то вроде

#define LOG(x) \
  cout << __FUNCTION__ << x << endl

// ...
LOG("My message with number " << number << " and some more");

Проблема с вашим подходом состоит в том (как объяснил Николай Голыбью), что FUNCTION обрабатывается во время компиляции и поэтому всегда печатает одно и то же имя с непроцессорным решением.

Если это только для добавления endl к вашим сообщениям, вы можете попробовать что-то вроде:

class MyLine {
public:
  bool written;
  std::ostream& stream;
  MyLine(const MyLine& _line) : stream(_line.stream), written(false) { }
  MyLine(std::ostream& _stream) : stream(_stream), written(false) { }
  ~MyLine() { if (!written) stream << "End of Message" << std::endl; }
};

template <class T> MyLine operator<<(MyLine& line, const T& _val) {
  line.stream << _val;
  line.written = true;
  return line;
}

class MyStream {
public:
  std::ostream& parentStream;
  MyStream(std::ostream& _parentStream) : parentStream(_parentStream) { }
  MyLine getLine() { return MyLine(parentStream); }
};

template <class T> MyLine operator<<(MyStream& stream, const T& _val) {
  return (stream.getLine() << _val);
}

int main()
{
      MyStream stream(std::cout);
      stream << "Hello " << 13 << " some more data";
      stream << "This is in the next line " << " 1 ";
    return 0;
}

Обратите внимание, что важно не возвращать ссылки из операторских функций. Поскольку MyLine должен существовать только как временный (поскольку его деструктор запускает запись endl), первый объект (возвращаемый функцией getLine() в MyStream) будет уничтожен до второго operator<< называется. Поэтому объект MyLine копируется в каждый operator<<, создавая новый. Последний объект уничтожается без записи и записи конца сообщения в его деструкторе.

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

0 голосов
/ 23 марта 2009

Вы должны переопределить оператор << (), но вам даже не нужно создавать подкласс std :: cout. Вы также можете создать новый объект или использовать существующие объекты, подобные этому. </p>

0 голосов
/ 23 марта 2009

Вы также можете переопределить оператор . Это позволит вам вызывать другую функцию или префикс / суффикс для всего, что будет выходить из буфера вывода с любым, что вы пожелаете: в вашем случае он будет выводить определенную строку.

...