下面是一些修改和注释,随着这个日志类的增长和变得更成熟,它们可能会有所帮助。
b) 将记录器与正在记录的内容解耦(否则,记录器必须了解整个应用程序和所有库)。
c) 提供一种机制,以便轻松允许任何类型的日志记录,即使由第三方库提供。
#include <iostream>
// I've put the logger and its helpers into a namespace. This will keep code tidy and help with
// ADL.
namespace logging
{
// define a general function which writes a value to a stream in "log format".
// you can specialise this for specific types in std:: if you wish here
template<class T>
void to_log(std::ostream& os, T const& value)
{
os << value;
}
// define a general function objects for writing a log-representation of tyoe T.
// There are 2 ways to customise this.
// a) provide a free function called to_log in the same namespace as your classes (preferred)
// b) specialise this class.
template<class T>
struct log_operation
{
void operator()(std::ostream& os, T const& value) const
{
to_log(os, value);
}
};
// specialise for any pointer
template<class T>
struct log_operation<T*>
{
void operator()(std::ostream& os, T* ptr) const
{
if (!ptr)
os << "null";
else
{
os << "->";
auto op = log_operation<std::decay_t<T>>();
op(os, *ptr);
}
}
};
// the logger is now written in terms of log_operation()
// it knows nothing of your application's types
class logger
{
public:
// Template for handling any type.
// not that this will also catch pointers.
// we will disambiguate in the log_operation
template< typename T >
logger& operator << ( const T& rBar )
{
auto op = log_operation<std::decay_t<T>>();
op(std::cout, rBar);
std::cout << std::endl;
return *this;
}
};
}
class someOtherClass
{
public:
someOtherClass()
: a(0)
, b(1.0f)
, c(2.0)
{}
int a;
float b;
double c;
};
// someOtherClass's maintainer provides a to_log function
void to_log(std::ostream& os, someOtherClass const& c)
{
os << "someOtherClass { " << c.a << ", " << c.b << ", " << c.c << " }";
}
namespace third_party
{
// the is in a 3rd party library. There is no to_log function and we can't write one which will be found with
// ADL...
struct classWhichKnowsNothingOfLogs {};
}
/// ..so we'll specialise in the logging namespace
namespace logging
{
template<>
struct log_operation<::third_party::classWhichKnowsNothingOfLogs>
{
void operator()(std::ostream& os, ::third_party::classWhichKnowsNothingOfLogs const& value) const
{
os << "classWhichKnowsNothingOfLogs {}";
}
};
}
int main(int argc, char* argv[])
{
logging::logger l;
someOtherClass soc;
someOtherClass* pSoc = &soc;
l << soc;
l << pSoc;
pSoc = nullptr;
l << pSoc;
l << third_party::classWhichKnowsNothingOfLogs();
return 0;
}
someOtherClass { 0, 1, 2 }
->someOtherClass { 0, 1, 2 }
null
classWhichKnowsNothingOfLogs {}