Tuesday, 23 September 2014

Variadic print_log function

The c++11 standard allows the easy implementation of functions with variadic arguments. This ability can be exploited to build a user friendly log printing function that can take a variable number of arguments of different types:
    ...   
    // armadillo linear algebra library - http://goo.gl/5UmGY  
    arma::vec v = arma::randu<vec>(4);                                                                              
                                                                              
    print_log("this is the ",1,"-st example." );
    print_log("\u03C0 = ", pi, " and \u03C0/2 =",pi/2.0 );
    print_log();
    print_log("a vector: ",v.t());
    ...
Output:
this is the 1-st example.
π = 3.14159 and π/2 =1.5708

a vector:    0.8402   0.3944   0.7831   0.7984


First we provide the atomic versions of the function print_log defined respectivelly for zero and one arguments. Then we overload them with a variadic version taking a simple argument plus a variadic argument. Within the variadic print_log the one-argument print_log  is first called on the simple argument. Then the variadic print_log itself is called on the variadic argument which it will split again into a single argument plus a variadic argument. Thus all arguments will be printed one by one throught the one-argument print_log.

We also define a terminator function calling the one-argument print_log with the line-feed string as the argument. This function will be always called at the end of the recursion in the variadic print_log.  A pre-processor condition allows to choose at compile time if logs are to be printed or not. This is done by implementing an empty one-argument print_log  if the macro NOLOG is defined.
#include <iostream>

using namespace std;

#ifdef NOLOG

// empty one-argument version
template<typename T>
inline void print_log(T&& t) {  }

// empty one-argument-no-endline version
template<typename T>
inline void print_log_not_ended(T&& t ) { }

#else

// one-argument version
template<typename T>
inline void print_log(T&& t ) { cout << t << endl;  }

// one-argument-no-endline version
template<typename T>
inline void print_log_not_ended(T&& t ) { cout << t; }

#endif

// termination version
inline void print_log() {  cout << endl;  }

// variadic version
template<typename Arg, typename... Args>
inline void print_log(Arg&& arg1, Args&&... args)
{
    print_log_not_ended(arg1);
    print_log(args...);
    if( sizeof...(args) < 1  )
        print_log();
}