A typical LOG() macro-based logging solution may look something like this:
#define LOG(msg) \\
std::cout << __FILE__ << \"(\" << __LINE__ &
Here is another expression template which seems to be even more efficient based on some tests that I've run. In particular, it avoids creating multiple functions for strings with different lengths by specializing operator<<
to use a char *
member in the resulting structure. It should also be easy to add other specializations of this form.
struct None { };
template <typename First,typename Second>
struct Pair {
First first;
Second second;
};
template <typename List>
struct LogData {
List list;
};
template <typename Begin,typename Value>
LogData<Pair<Begin,const Value &>>
operator<<(LogData<Begin> begin,const Value &value)
{
return {{begin.list,value}};
}
template <typename Begin,size_t n>
LogData<Pair<Begin,const char *>>
operator<<(LogData<Begin> begin,const char (&value)[n])
{
return {{begin.list,value}};
}
inline void printList(std::ostream &os,None)
{
}
template <typename Begin,typename Last>
void printList(std::ostream &os,const Pair<Begin,Last> &data)
{
printList(os,data.first);
os << data.second;
}
template <typename List>
void log(const char *file,int line,const LogData<List> &data)
{
std::cout << file << " (" << line << "): ";
printList(std::cout,data.list);
std::cout << "\n";
}
#define LOG(x) (log(__FILE__,__LINE__,LogData<None>() << x))
With G++ 4.7.2, with -O2 optimization, this creates a very compact instruction sequence, equivalent to filling a struct with the parameters using a char *
for string literals.
I've gone through exactly the same thing. And I ended up with the same solution you've outlined, which just requires the client API to use comma instead of the insertion operator. It keeps things fairly simple, and works well enough. Highly recommended.