Как перегрузить оператор<<, который не принимает и не возвращает ostream
-
08-07-2019 - |
Вопрос
Оригинальный вопрос
Я пишу класс ведения журнала, цель которого - сделать это:
// thread one
Logger() << "Some string" << std::ios::hex << 45;
// thread two
Logger() << L"Some wide string" << std::endl;
В настоящее время мой заголовок Logger выглядит примерно так:
#pragma once;
#include <ostream>
class Logger
{
public:
Logger();
~Logger();
std::ostream* out_stream;
};
template <typename T>
Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
Некоторые замечания об этом классе:
- Кроссплатформенная совместимость не является проблемой.
- Внутри Logger.cpp есть одноэлементный класс, который занимается созданием «настоящего» ostream.
- Конструктор и деконструктор Logger выполняют необходимую блокировку синглтона.
У меня три проблемы:
- Как сделать функцию оператора<< другом или участником, чтобы я мог установить out_stream как частный?
- Как заставить функциюoperator<< работать для манипуляторов?
- Как я могу добавить специализацию, чтобы, если T является WCHAR* или std::wstring, он конвертировал его в char* или std::string перед передачей его в out_stream?(Я могу сделать конвертацию.В моем случае потеря символов Юникода не является проблемой.)
Краткое изложение того, что узнали в ответах:
- Поместите шаблон ДО друга, а не после.
- std::ios::hex не является манипулятором.std::hex — манипулятор.
Конечный результат
#pragma once
#include <ostream>
#include <string>
std::string ConvertWstringToString(std::wstring wstr);
class Logger
{
public:
Logger();
~Logger();
template <typename T>
Logger& operator<< (T data) {
*out << data;
return *this;
}
Logger& operator<< (std::wstring data) {
return *this << ConvertWstringToString(data);
}
Logger& operator<< (const wchar_t* data) {
std::wstring str(data);
return *this << str;
}
private:
std::ostream* out;
};
Решение
Вы можете использовать определение друга, которое определит оператор в окружающем пространстве имен класса и сделает его видимым только для разрешения перегрузки оператора (нельзя вызывать вручную с помощью ::operator<<...синтаксис):
class Logger
{
public:
Logger();
~Logger();
std::ostream* out_stream;
template <typename T>
friend Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
/* special treatment for std::wstring. just overload the operator! No need
* to specialize it. */
friend Logger& operator<< (Logger& logger, const std::wstring & wstr) {
/* do something here */
}
};
В качестве альтернативы, чтобы сохранить свой код в том виде, в каком он есть, и просто сделать шаблон оператора<< другом, вы добавляете эту строку в определение своего класса:
template <typename T>
friend Logger& operator<< (Logger& logger, T thing);
Что касается проблемы манипулятора, я просто приведу вам свой код, который я написал некоторое время назад:
#include <iostream>
#include <cstdlib>
using namespace std;
template<typename Char, typename Traits = char_traits<Char> >
struct logger{
typedef std::basic_ostream<Char, Traits> ostream_type;
typedef ostream_type& (*manip_type)(ostream_type&);
logger(ostream_type& os):os(os){}
logger &operator<<(manip_type pfn) {
if(pfn == static_cast<manip_type>(std::endl)) {
time_t t = time(0);
os << " --- " << ctime(&t) << pfn;
} else
os << pfn;
return *this;
}
template<typename T>
logger &operator<<(T const& t) {
os << t;
return *this;
}
private:
ostream_type & os;
};
namespace { logger<char> clogged(cout); }
int main() { clogged << "something with log functionality" << std::endl; }
};
Обратите внимание, что это std::hex , а не std::ios::hex..Последний используется как флаг манипулятора для setf
функция потоков.Обратите внимание, что для вашего примера никакого специального обращения с манипуляторами не требуется.Вышеупомянутая специальная обработка std::endl необходима только потому, что я дополнительно передаю время, когда используется std::endl.
Другие советы
Использование шаблона — правильный способ сделать это, но вам просто нужно убедиться, что шаблон находится в заголовок файл (logger.h
, или как вы там называли), нет в файле реализации (logger.cpp
).Это будет автоматически работать для любого типа, который имеет operator <<
определяется с помощью std::ostream
.Он также будет автоматически работать с объектами манипулятора потока — на самом деле это просто функции, которые принимают std::ostream
параметр и operator <<
просто вызывает функцию на ostream
.
Ты можешь сделать operator <<
друг действует следующим образом:
template <typename T> friend Logger& operator<< (Logger& logger, T thing);
Специализации просты — просто используйте специализации шаблона (опять же, в заголовочном файле):
template <typename T>
Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
// Template specialization - the "template <>" part is necessary
template <>
Logger& operator<< (Logger& logger, const wchar_t *wstr)
{
// convert wstr to an ANSI string and log it
}
template <>
Logger& operator<< (Logger& logger, const std::wstring & wstr)
{
// convert wstr to an ANSI string and log it
}
Декларация о дружбе не требуется:
class Logger
{
public:
Logger();
~Logger();
template <typename T>
inline Logger& Display(T thing)
{
*out_stream << thing;
return *this;
}
private:
std::ostream* out_stream;
};
template <typename T>
Logger& operator<< (Logger& logger, T thing)
{
return logger.Display(thing);
}
почему бы не сделать это способом printf и использовать метод с несколькими параметрами (с тремя точками...).Это по-прежнему дает вам большие возможности форматирования и не делает его таким беспорядочным, как при использовании <<.
Например:
Logger("This is my log msg %0X", 45);
Подождите две секунды, и я подниму вам образец кода.
Редактировать:
void Logger(const char* format, ...)
{
char szMsg[3000];
va_list args;
va_start( args, format );
vsnprintf( szMsg, sizeof(szMsg) - 1, format, args );
va_end(args);
// code to print szMsg to a file or whatever here
}
Если вы хотите использовать это как класс, а не отдельную функцию, вы можете перегрузить оператор журнала (), и он будет работать точно так же.