Created
August 30, 2015 23:24
-
-
Save Redchards/53e024b70979e7ebd51b to your computer and use it in GitHub Desktop.
First attempt to create a policy-based exentsible and fast logging system I did some time ago. It failed, but gave a strong basis for another try (see github repository)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef COMMON_CORE_UTILITY_LOGGER | |
#define COMMON_CORE_UTILITY_LOGGER | |
#include <iostream> | |
/* This logger library attempts to be simple, but as extensible as possible, and as | |
* easy to use as possible. Performance is also taken in account, even if it's not | |
* the main reason for writing this. | |
* The ultimate goal is to achieve a clean interface, as easy to use as possible | |
* without any use of macro, so it feels more like modern C++, and greatly reduce | |
* name collision risks ! | |
* The interface is : | |
* Log<MyWritePolicy, Level::Info> myLog; // Level::Info is the minimal verbose level | |
* myLog(Level::Warning) << "MyMessage" << " Is awesome !!"; | |
* | |
* A filter can also be used like that : | |
* Log<Level::Debug> myLogFilter(myLog); | |
* // You can now used myLogFilter as if it was a logger | |
* myLogFilter(Level::Fatal) << "You don't know how to program you moron !!!"; | |
* | |
* Moreover, this log is aimed to run asynchronously, thus improving performance when in | |
* multithreaded environnement. But, because of the creation of intermediates objects, it | |
* will be less efficient in single threaded environnement than a synchronous logger like | |
* templog | |
* | |
* This logger allow considitonal logging with nice interface too, for example : | |
* Log(Level::Info).If(myVar == 5) << "This will be logged only if myVar = 5 ! :) "; | |
* //if the value of myVar is known at compile time, message can be discarded at compile time | |
* Log(Level::Info).If([&var]{ var = 5;}) << "Same as above but with lambda expressions"; | |
* //lambda expressions support for more possibilities | |
* Log(Level::Info).If(&predicate) << "Conditional logging with delegates ! ";*/ | |
// TODO: remove the String const& overload in WritePolicy. In fact, Format will return temp, so always moving | |
// ABSOLUTE TODO: String cause ENORMOUS CODE BLOAT !!! Temporary strings should be const char* ! | |
// ONLY FINAL MESSAGE SHOULD BE A STRING ! | |
// This also show us the interest of creating our own and very simple string class :) | |
enum class Verbose : uint8_t { Debug = 1, | |
Info, | |
Message, | |
Warning, | |
Error, | |
Fatal }; | |
//This is ugly, but it will stay like that untile this dumb language allow us to pass constexpr arguments | |
//to constexpr expressions :/ This is the best way to discard log messages without macros | |
struct VerboseLevel | |
{ | |
static std::integral_constant<Verbose, Verbose::Warning> Warning; | |
static std::integral_constant<Verbose, Verbose::Fatal> Fatal; | |
}; | |
template<bool Buffered> | |
class IWritePolicy {}; | |
template<class WritePolicy, class FormatPolicy> | |
class IPolicy {}; | |
template<> | |
class IWritePolicy<true> | |
{ | |
public: | |
IWritePolicy() = default; | |
void Write(String&& msg); | |
void Write(String const& msg); | |
// Allow thread safety | |
// If the implementation is empty, will just do nothing and ignore thread safety | |
// Must be called before writing ! | |
void LockWrite() | |
{} | |
void UnlockWrite() | |
{} | |
enum{ buffered = true }; | |
enum{ dynamic = false }; | |
}; | |
template<> | |
class IWritePolicy<false> | |
{ | |
public: | |
IWritePolicy() = default; | |
static void Write(String&& msg); | |
static void Write(String const& msg); | |
static void LockWrite() | |
{} | |
static void UnlockWrite() | |
{} | |
enum{ buffered = false }; | |
enum{ dynamic = false }; | |
}; | |
class IFormatPolicy | |
{ | |
public: | |
IFormatPolicy() = default; | |
static String Format(String&&, const Verbose); | |
static String Format(String const&, const Verbose); | |
enum{ dynamic = false }; | |
}; | |
class IWritePolicyIntermediate | |
{ | |
public: | |
IWritePolicyIntermediate() = default; | |
virtual ~IWritePolicyIntermediate() = default; | |
virtual void Write(String&& msg) = 0; | |
virtual void Write(String const& msg) = 0; | |
}; | |
template<class WritePolicy, typename Enabler = void> | |
class WritePolicyIntermediate; | |
template<class WritePolicy> | |
class WritePolicyIntermediate<WritePolicy, | |
typename std::enable_if<WritePolicy::buffered::type>::result::type> : IWritePolicyIntermediate // Private inheritance, nothing to be inherited in fact | |
{ | |
public: | |
WritePolicyIntermediate() = default; | |
virtual ~WritePolicyIntermediate() = default; | |
virtual void Write(String&& msg) | |
{ | |
writePolicy_.template Write(std::move(msg)); | |
} | |
virtual void Write(String const& msg) | |
{ | |
writePolicy_.template Write(msg); | |
} | |
enum{ buffered = true }; | |
private: | |
WritePolicy writePolicy_; | |
}; | |
template<class WritePolicy> | |
class WritePolicyIntermediate<WritePolicy, | |
typename std::enable_if<!WritePolicy::buffered::type>::result::type> : IWritePolicyIntermediate | |
{ | |
public: | |
WritePolicyIntermediate() = default; | |
virtual ~WritePolicyIntermediate() = default; | |
virtual void Write(String&& msg) | |
{ | |
WritePolicy::template Write(std::move(msg)); | |
} | |
virtual void Write(String const& msg) | |
{ | |
WritePolicy::template Write(msg); | |
} | |
enum { buffered = false }; | |
}; | |
class IFormatPolicyIntermediate | |
{ | |
public: | |
IFormatPolicyIntermediate() = default; | |
virtual ~IFormatPolicyIntermediate() = default; | |
virtual String Format(String&&, const Verbose) = 0; | |
virtual String Format(String const&, const Verbose) = 0; | |
}; | |
template<class FormatPolicy> | |
class FormatPolicyIntermediate : public IFormatPolicyIntermediate | |
{ | |
public: | |
FormatPolicyIntermediate() = default; | |
virtual ~FormatPolicyIntermediate() = default; | |
virtual String Format(String&& msg, Verbose const ver) | |
{ | |
return FormatPolicy::template Format(std::move(msg), ver); | |
} | |
virtual String Format(String const& msg, Verbose const ver) | |
{ | |
return FormatPolicy::template Format(msg, ver); | |
} | |
}; | |
class BufferedWritePolicy : public IWritePolicy<true> {}; | |
class NonBufferedWritePolicy : public IWritePolicy<false> {}; | |
template<class WritePolicy> | |
class DynamicWritePolicy | |
{ | |
public: | |
DynamicWritePolicy() | |
{ | |
writePolicy_ = new WritePolicyIntermediate<WritePolicy>(); | |
} | |
void Write(String&& msg) | |
{ | |
writePolicy_->Write(std::move(msg)); | |
} | |
void Write(String const& msg) | |
{ | |
writePolicy_->Write(msg); | |
} | |
template<class NewPolicy> | |
void SetPolicy() | |
{ | |
writePolicy_ = new WritePolicyIntermediate<WritePolicy>(); | |
} | |
enum{ dynamic = true }; | |
protected: | |
IWritePolicyIntermediate* writePolicy_; | |
}; | |
template<class FormatPolicy> | |
class DynamicFormatPolicy | |
{ | |
public: | |
DynamicFormatPolicy() | |
{ | |
formatPolicy_ = new FormatPolicyIntermediate<FormatPolicy>(); | |
} | |
// Do the same for writepolicy if it works ? | |
String Format(String&& msg, Verbose const ver) | |
{ | |
return formatPolicy_->Format(std::move(msg), ver); | |
} | |
String Format(String const& msg, Verbose const ver) | |
{ | |
return formatPolicy_->Format(msg, ver); | |
} | |
template<class NewPolicy> | |
void SetPolicy() | |
{ | |
formatPolicy_ = new FormatPolicyIntermediate<FormatPolicy>(); | |
} | |
enum{ dynamic = true }; | |
protected: | |
IFormatPolicyIntermediate* formatPolicy_; | |
}; | |
/*template<class WritePolicy, class FormatPolicy> | |
class ILogPolicy | |
{ | |
public: | |
ILogPolicy() = default; | |
template<Verbose Ver, Target ... Targets, typename T> | |
void Write(T&& msg); | |
};*/ | |
//This is hell lot of class for a simple feature. | |
//However, with my design this is necessary to achieve high degree of syntactic sugar and extensibility. | |
//Theses class are not meant to be extended. The user can extend the whole logger by writing other | |
//writePolicies and formatPolicies. Then, it is easy to build a Policy by passing a writePolicy and a | |
//formatPolicy to a LogPolicy and typedef it. | |
//So I know that lot people will dislike this part of the code but hey, an ugly code that do things well and | |
//give maximum extensibility is not bad code, just ugly code :) | |
// Fortunatly, classes are not big ! | |
// Maybe add WritePolicy::LockWrite() and UnlockWrite() when calling write policy ? | |
// We should definitly add more capacity in defining when we stop writing | |
template<class WritePolicy, class FormatPolicy> | |
class NoMember | |
{ | |
public: | |
NoMember() = default; | |
static void Foo(String&& msg, Verbose ver) | |
{} | |
static void Write(String&& msg, Verbose ver) | |
{ | |
WritePolicy::Write(FormatPolicy::Format(std::forward<String>(msg), ver)); | |
} | |
}; | |
template<class WritePolicy, class FormatPolicy> | |
class WriteDynamicMember | |
{ | |
public: | |
WriteDynamicMember() = default; | |
void Foo(String&& msg, Verbose ver) | |
{} | |
void Write(String&& msg, Verbose ver) | |
{ | |
writePolicy_.Write(FormatPolicy::Format(std::forward<String>(msg), ver)); | |
} | |
template<class NewPolicy> | |
void SetWritePolicy() | |
{ | |
writePolicy_.template SetPolicy<NewPolicy>(); | |
} | |
WritePolicy writePolicy_; | |
}; | |
template<class WritePolicy, class FormatPolicy> | |
class FormatDynamicMember | |
{ | |
public: | |
FormatDynamicMember() = default; | |
void Write(String&& msg, Verbose ver) | |
{ | |
/*using l = Builder<Targets...>; | |
std::array<Target, l::result.size()> targetList = l::result; | |
std::cout << (int)(targetList[0]) << " : " << (int)(targetList[1]) << " : " << (int)(targetList[2]) << " d" << (int)Target::User << " size : " << targetList.size();*/ | |
WritePolicy::Write(formatPolicy_.Format(std::forward<String>(msg), ver)); | |
} | |
template<class NewPolicy> | |
void SetFormatPolicy() | |
{ | |
formatPolicy_.template SetPolicy<NewPolicy>(); | |
} | |
FormatPolicy formatPolicy_; | |
}; | |
template<class WritePolicy, class FormatPolicy> | |
class BothDynamicMember | |
{ | |
public: | |
BothDynamicMember() = default; | |
void Write(String&& msg, Verbose ver) | |
{ | |
writePolicy_.Write(formatPolicy_.Format(std::forward<String>(msg), ver)); | |
} | |
template<class NewPolicy> | |
void SetWritePolicy() | |
{ | |
writePolicy_.template SetPolicy<NewPolicy>(); | |
} | |
template<class NewPolicy> | |
void SetFormatPolicy() | |
{ | |
formatPolicy_.template SetPolicy<NewPolicy>(); | |
} | |
WritePolicy writePolicy_; | |
FormatPolicy formatPolicy_; | |
}; | |
/* Used to disable string creation when logger is disabled | |
Otherwise, the code will be bloated even if the logger is | |
disabled. As we want to discard messages completly at compile time | |
if we know value, this is a good way I think */ | |
class FalseString | |
{ | |
public: | |
FalseString(const char*){}; | |
}; | |
class DummyLog | |
{ | |
public: | |
DummyLog operator<<(FalseString&&){ return *this; }; | |
DummyLog If(bool){ return *this; }; | |
static DummyLog instance; | |
}; | |
template<class WritePolicy, class FormatPolicy> | |
class LogPolicy : public std::conditional<!(WritePolicy::buffered || WritePolicy::dynamic) && !FormatPolicy::dynamic, | |
NoMember<WritePolicy, FormatPolicy>, | |
typename std::conditional<(WritePolicy::dynamic || WritePolicy::buffered) && !FormatPolicy::dynamic, | |
WriteDynamicMember<WritePolicy, FormatPolicy>, | |
typename std::conditional<!(WritePolicy::dynamic || WritePolicy::buffered) && FormatPolicy::dynamic, | |
FormatDynamicMember<WritePolicy, FormatPolicy>, | |
BothDynamicMember<WritePolicy, FormatPolicy>>::type>::type>::type | |
{ | |
public: | |
constexpr LogPolicy() = default; | |
template<class T> | |
constexpr LogPolicy<WritePolicy, FormatPolicy>& operator<<(T&& msg) const | |
{ | |
} | |
}; | |
class BaseFormatPolicy : public IFormatPolicy | |
{ | |
public: | |
BaseFormatPolicy() = default; | |
static String Format(String&& msg, Verbose const ver) | |
{ | |
return ("< yo bymove: " + msg + "\n"); | |
} | |
static String Format(String const& msg, Verbose const ver) | |
{ | |
return ("< yo bycall: " + msg + "\n"); | |
} | |
}; | |
class BaseWritePolicy : public NonBufferedWritePolicy | |
{ | |
public: | |
BaseWritePolicy() = default; | |
static void Write(String&& msg) | |
{ | |
printf("%s", msg.c_str()); | |
} | |
static void Write(String const& msg) | |
{ | |
printf("%s", msg.c_str()); | |
} | |
}; | |
// Check copy and move constructor (to add or not to add ?) | |
template<class LogPolicy> | |
class Log; | |
template<class LogPolicy> | |
class Hold1 | |
{ | |
public: | |
typedef void (LogPolicy::*pf)(String&&, Verbose); | |
constexpr Hold1(Log<LogPolicy>* sq, pf dq) : p(sq), d(dq) {} | |
Log<LogPolicy>* p; | |
pf d; | |
constexpr Hold1<LogPolicy>& operator<<(String&& msg) | |
{ | |
(p->*d)(std::move(msg), Verbose::Warning); | |
return *this; | |
} | |
}; | |
/* | |
* template<int ...> | |
struct seq { }; | |
template<int N, int ...S> | |
struct gens : gens<N-1, N-1, S...> { }; | |
template<int ...S> | |
struct gens<0, S...> { | |
typedef seq<S...> type; | |
}; | |
// ... | |
void delayed_dispatch() { | |
callFunc(typename gens<sizeof...(Args)>::type()); | |
} | |
template<int ...S> | |
void callFunc(seq<S...>) { | |
func(std::get<S>(params) ...); | |
} | |
// ...*/ | |
template<class LogPolicy> | |
class Hold2 | |
{ | |
public: | |
typedef void (*pf)(String&&, Verbose); | |
constexpr Hold2(Log<LogPolicy>* sq, pf dq) : p(sq), d(dq) {} | |
Log<LogPolicy>* p; | |
pf d; | |
constexpr Hold2<LogPolicy>& operator<<(const char* msg) | |
{ | |
LogPolicy::Write(std::move(msg), Verbose::Warning); | |
return *this; | |
} | |
constexpr Hold2<LogPolicy>& operator<<(String&& msg) | |
{ | |
LogPolicy::Write(std::move(msg), Verbose::Warning); | |
return *this; | |
} | |
/*constexpr auto If(bool const c) | |
{ | |
if(c) return *this; | |
else return *p; | |
}*/ | |
}; | |
template<class LogPolicy> | |
class Holder : public std::conditional<std::is_base_of<NoMember<BaseWritePolicy, BaseFormatPolicy>, LogPolicy>::value, | |
Hold2<LogPolicy>, Hold1<LogPolicy>>::type | |
{ | |
public: | |
static_assert(std::is_base_of<NoMember<BaseWritePolicy, BaseFormatPolicy>, LogPolicy>::value, "NOPE!"); | |
typedef typename std::conditional<std::is_base_of<NoMember<BaseWritePolicy, BaseFormatPolicy>, LogPolicy>::value, | |
Hold2<LogPolicy>, Hold1<LogPolicy>>::type Der; | |
Holder(Log<LogPolicy>* sq, typename Der::pf dq) : Der(sq, dq) {} | |
}; | |
template<bool v> | |
class Forwarder | |
{ | |
}; | |
/*class Inter | |
{ | |
public: | |
constexpr | |
};*/ | |
/*template<typename T, class LogPolicy> | |
class La | |
{}; | |
class La<Holder>*/ | |
template<class LogPolicy> | |
class Log | |
{ | |
public: | |
constexpr Log() : logPolicy_(){}; | |
Log& t(Forwarder<false> me) | |
{ | |
return *this; | |
} | |
Holder<LogPolicy> t(Forwarder<true> me) | |
{ | |
return Holder<LogPolicy>(this, &LogPolicy::Write); | |
} | |
constexpr Log& operator<<(FalseString&& msg) | |
{ | |
return *this; | |
} | |
template<Verbose Level> | |
inline constexpr auto operator()(std::integral_constant<Verbose, Level> ver) // Stupid C++, allow me to pass constexpr !!! | |
{ | |
return t(Forwarder<ver == Verbose::Warning>()); | |
//return ver == Verbose::Warning ? Holder<LogPolicy>(this, &LogPolicy::Write) : Holder<LogPolicy>(this, &LogPolicy::Foo); | |
} | |
private: | |
const LogPolicy logPolicy_; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment