Исходный вопрос
Я пишу класс ведения журнала, цель которого - сделать это:
// 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 есть класс singleton, который заботится о создании «настоящего» ostream.
- Конструктор и деконструктор Logger выполняют необходимую блокировку синглтона.
У меня три проблемы:
- Как мне сделать функцию operator‹‹ другом или членом, чтобы я мог установить out_stream как частный?
- Как заставить функцию оператора‹‹ работать для манипуляторов?
- Как я могу добавить специализацию, чтобы, если 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;
};