C++ Engine Layout

Started by
9 comments, last by vreality 11 years, 4 months ago

Logging is the classic example of the thing you make that everything needs access to, because it's the only example (unless you're re-implementing other environmental features, like the memory manager, etc.)

On 12/18/2012 at 9:12 PM, Hodgman said:

Personally, I like to treat it specifically as an exception to the rule, because I don't see it so much as part of your application, but instead it's a part of the environment in which you're building your application.

I don't know how Hodgman manages to be right every single time, but once again, Hodgman's right. Logging is the exception because it's diagnostic scaffolding which is unrelated to our code's actual functionality.

But even so, it's possible to come up with a reasonable design for it.

Our design goals might be:

  • Support multiple logging channels, locally configurable to be sent to various destinations.
  • No logger object needed at point of logging (i.e. no passing loggers to every single function).
  • No globally accessible state.

class Logger
{
  public: // "Object part"
    void SendChannelToFile (string const & Channel, string const & FileName);
    void SendChannelToDebugConsole (string const & Channel);
    void SendChannelToOnScreenConsole (string const & Channel);
    ...

  public: // "Global part"
    static void Log(string const & Channel, string const & Message);
    ...
};

The basic idea is that Logger::Log() has some sort of safe default functionality (like ignoring all log messages), but if a Logger object exists, then the function uses that object's configuration information to route any configured channels.

Note that the globally accessible Logger::Log() function neither sets nor gets state.

The appropriate part of the codebase can create a Logger object, and load a local configuration file to configure logging channels according to the local user's current preference. Setup, access, lifetime, and cleanup of the object and its configuration data are all managed as with any normal object. The Logger class can prevent more than one instance from being instantiated, to avoid mishaps.

The Logger object's internal state does remotely effect the behavior of Logger::Log(), but then Logger::Log() is generally designed to effect the environment outside of the application (like printf() and std::clog() ).

And yes, the connection between the global and object parts would be accomplished through some static pointer, and Logger::Log() may need to be thread-proofed. But again, this system is the exception, and all of the above functions can be conditionally compiled to no-ops in published versions.

This topic is closed to new replies.

Advertisement