The result of this latest redesign is ready, and you can get it here.
lib_logger.hhas grown quite a bit. In order to properly take advantage of
__LINE__I needed to go with macros. Lots of macros, but the work in creating all these macros was softened by a little bit of perl scripting.
Note: I considered using
__func__, as well, but since POCO's Logger doesn't support it, I'd have to manage that in my own Logger. However, I believe that my Logger's design goals don't include this kind of management; it's a bridge to an actual implementation, it should own nothing, and it should impose little to no unwanted weight on the client code. So, I've decided that if the client code needs
__func__, it'll have to take care of it.
__LINE__should be enough to follow a trail, if needed.
Another reason for this macro growth was that I wanted this to work with both GCC and MSVC. There is a difference between
LoggerBridge<bool condition, etc>and the
LoggerBridge<false, etc>specialization - the latter has no template functions. This poses no problem with GCC; since template functions have default template parameters, the call is similar for both template and non-template functions. On MSVC, however, template functions don't have default template parameters. Therefore, the call is
object.function<type>()for one and
object.function()for the other.
My first instinct was te change the specialization, changing its functions to template functions. But, after considering it further, I decided not to. Default template parameters for template functions are in the Standard, and (hopefully) sooner or later will also be in MSVC. Until then, the MSVC code will have more preprocessor "magic".
I've tested this in Qt Creator and MSVC, with my Logger (wrapper around POCO's Logger) and LoggerCout (minimum interface implementation), with values of 1 and 0 for
WANT_LOGGING. All went as expected.
So, when all is said and done, what are the requirements for a client app that wants to replace the default Logger? Look at class LoggerCout for the minimum interface:
explicit LoggerCout(std::string const&, char const* = nullptr);
static void CreateLogger(std::string const&, std::string const&);
bool IsCritical() const;
bool IsDebug() const;
bool IsError() const;
bool IsFatal() const;
bool IsInformation() const;
bool IsNotice() const;
bool IsTrace() const;
bool IsWarning() const;
void Critical(std::string const& msg);
void Debug(std::string const& msg);
void Error(std::string const& msg);
void Fatal(std::string const& msg);
void Information(std::string const& msg);
void Notice(std::string const& msg);
void Trace(std::string const& msg);
void Warning(std::string const& msg);
I don't think that's unreasonable. Of course, any other functions you need will have to be added to LoggerBridge manually. Since I've started this redesign, I've seen Herb Sutter's 2012 concurrency presentation, and was intrigued by the wrapper design he presented, especially the part about the wrapper being completely oblivious to the wrapee's interface. I'll certainly investigate if I can adapt it here, although the absence of inlining (via the use of functors/function pointers) may make it unsuitable. Anyway, for now, this is what we have.
There's one final question I'll need to answer, and that's what I'll be working on next: How does this affect performance? I'll prepare some testing with the following scenarios:
- No logging
- Logging with this solution
- Bypassing this solution and logging directly with POCO's Logger
As variations, some runs will have trivial processing, where logging should take up most of the work, and in others I'll simulate some heavy work, probably by use of
sleep(). And I'll also test the use of POCO's AsyncChannel. This will give me some idea of what to expect.
Yep, I know, I finish a lot of these posts with a "Next, I'll do such and such". The truth is there's always work to be done. I like it that way.