I've created a simple logging system that I'm planning to use and I've tried out some modern C++ features to make it generic. First I've made a log class that represents a single log message:
struct log : std::stringstream
{
private:
template <typename Level, size_t I = 0, typename... Stamps>
static inline typename std::enable_if<I == sizeof...(Stamps), void>::type
__stamp(log& l)
{
}
template <typename Level, size_t I = 0, typename... Stamps>
static inline typename std::enable_if<I < sizeof...(Stamps), void>::type
__stamp(log& l)
{
l << std::tuple_element_t<I, std::tuple<Stamps...>>::Get<Level>();
__stamp<Level, I + 1, Stamps...>(l);
}
public:
template <typename Level, typename... Stamps>
static log& emit()
{
static log l;
__stamp<Level, 0, Stamps...>(l);
l << ": ";
return l;
}
template <typename T>
log& operator<<(T el)
{
*((std::stringstream*)this) << el;
return *this;
}
template <typename Logger>
void Flush()
{
Logger::Log(str().c_str());
clear();
}
};
template <typename Logger>
std::ostream& endlog(std::ostream& out)
{
((log*)&out)->Flush<Logger>();
return out;
}
typedef Logger<ConsoleSink> DefaultLogger;
std::ostream& endlog(std::ostream& out)
{
((log*)&out)->Flush<DefaultLogger>();
return out;
}
And I've created the main logger:
template <typename... Sinks>
class Logger
{
private:
static std::tuple<Sinks...> s_Sinks;
private:
template <size_t I = 0, typename... Tp>
static inline typename std::enable_if<I == sizeof...(Tp), void>::type
__Log(const char* msg)
{
}
template <size_t I = 0, typename... Tp>
static inline typename std::enable_if<I < sizeof...(Tp), void>::type
__Log(const char* msg)
{
std::get<I>(s_Sinks).Write(msg);
__Log<I + 1, Tp...>(msg);
}
public:
static void Log(const char* msg)
{
__Log<0, Sinks...>(msg);
}
};
template <typename... Sinks>
std::tuple<Sinks...> Logger<Sinks...>::s_Sinks;
An example usage would be:
log::emit<Info, LevelStamp, SeparatorStamp, TimeStamp>() << "Hello, Logging " << 3 << endlog;
Now I can see a lot of problems with my own code, for example because of my emit function is defined with variadic stamps, there's no way to define a default overload for it so if I always want the same stamps, I have to type them out. Another is that my DefaultLogger is not configurable from outside the logger code which I think should be a feature.
Is my approach any good? How could I improve it?