Как я могу вывести класс из cout
, так что, например, запись на негоподгоняет COUT
new_cout << "message";
будет эквивалентно
cout << __FUNCTION__ << "message" << "end of message" << endl;
Как я могу вывести класс из cout
, так что, например, запись на негоподгоняет COUT
new_cout << "message";
будет эквивалентно
cout << __FUNCTION__ << "message" << "end of message" << endl;
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;
Вы могли бы также override the operator. Это позволит вам вызывать другую функцию или префикс/суффикс что-либо, что оставит выходной буфер с тем, что вы пожелаете: в вашем случае вы должны вывести определенную строку.
-1.При переопределении оператора создается верхний и нижний колонтитулы для каждого элемента, отформатированного: например. "new_cout << 1 << 2 << 3;" будет создавать 3 верхних и нижних колонтитула. –
Да, мои предложения предполагают, что человек, который пишет переопределение, знает, где это должно быть сделано, для каких типов и что его код действительно использует его правильно. –
Вам необходимо переопределить оператора < <(), но вы даже не должны подкласса std :: cout. Вы также можете создать новый объект или использовать существующие объекты, подобные этому.
Далее от ответа Мыкола, я имею следующую реализацию в моем коде. Использование является
LOG_DEBUG("print 3 " << 3);
печатает
DEBUG (f.cpp, 101): print 3 3
Вы можете изменить его, чтобы использовать FUNCTION вдоль/вместо ЛИНИИ и ФАЙЛ
/// 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;
}
}
#define debug_print(message) (std::cout << __FUNCTION__ << (message) << std::endl)
Это имеет то преимущество, что вы можете отключить все отладочные сообщения на один раз, когда вы сделали
#define debug_print(message)()
Для целей лесозаготовительной я использую что-то вроде
#define LOG(x) \
cout << __FUNCTION__ << x << endl
// ...
LOG("My message with number " << number << " and some more");
Проблемы с вашим подходом является (как Микола Golybyew пояснил), что ФУНКЦИЯ обрабатывается во время компиляции и поэтому всегда печатает одно и то же имя с помощью не препроцессорного решения.
Если это только для добавления 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<<
, создавая новый. Последний объект разрушается без написания и называет конец сообщения в своем деструкторе.
Просто попробуйте в отладчике, чтобы понять, что происходит ...
+1. В отличие от всех ответов, которые просто говорят «переопределить оператор <<()», это решение устраняет тот факт, что верхний и нижний колонтитулы должны выводиться один раз за оператор, а не один раз за <<. –
+1 действительно, хорошая идея. просто что-то о endl: я подозреваю, что для отладочного сообщения, endl редко требуется, но если вы хотите, чтобы он мог вносить его в волшебный журнал, поставьте оператор Log & operator << (ostream & (* f) (ostream &)) { cout << * f; return * this; }. –
@Earwicker. Спасибо за хорошее решение. Пожалуйста, объясните, что значит создавать экземпляр класса без создания объектной переменной. Я новичок в C++ и никогда не видел этого раньше. – jackhab