Saturday, May 11, 2013

Variadic Templates are Hip.


/**
 * Good morning. I missed yesterday so I'll be putting up
 * another post this afternoon to make up for the post
 * missed yesterday.
 *
 * Today I'm focusing a bit more on variadic templates.
 * Yesterday's example (or maybe the day before) had
 * a print function in it that looked kind of like this:
 *
 * template <typename T1, typename T2, typename T3>
 * void print(T1 const& t1, T2 const& t2, T3 const& t3)
 * {
 * mutex.lock();
 * std::cout << t1 << t2 << t3 << std::endl;
 * mutex.unlock();
 * }
 *
 * That's just plain sloppy for a couple reasons.
 * First, we should have created some sort of object to
 * do the streaming and not put it all into a method.
 * Second, what if people want to print 4 things instead
 * of three? Or maybe just 1 thing? They can't use this.
 *
 * The solution is remarkably simple: variadic templates.
 * These are REALLY HIP and let me tell you why: they
 * reduce sloppy code like our print code. Another example:
 *
 * std::pair<int, std::string>
 *
 * Yuck. In the past other tuple libraries have come
 * into existence. They all kind of covered up the
 * inadequacy of the C++ language to handle multiple
 * template parameters!
 *
 * For me this is also an experiment to see if libraries
 * built with a different compiler will work with clang's
 * C++ compiler (I believe the other libraries were
 * built with gcc or some standard compiler, definitely
 * not clang).
 */

// Include this, because we are making going to guard
// a print statement.
#include <boost/thread.hpp>

// For the print statement.
#include <iostream>
#include <string>


// Use a recursive mutex, this will be more apparent
// when you read a little more.
static boost::recursive_mutex mtx;


/**
 * The first interesting part of this example is this
 * print function with 1 template parameter. This
 * function is used as the base case for a recursive
 * function setup [1].
 */
template <typename T>
void print(T const& t)
{
mtx.lock();
std::cout << t;
mtx.unlock();
}

//#define SEE_THE_ACTION


/**
 * Now here's the HIP part. The second parameter uses
 * a "typename..." to specify that the Inputs type
 * can contain more than one template! Down below
 * that the input to the function reads
 * "Input... parameters". This function is like any
 * other- the compiler is wrapping a group of parameters
 * together into one parameter.
 */
template <typename T, typename... Inputs>
void print(T const& t, Inputs... parameters)
{

mtx.lock();

#if defined SEE_THE_ACTION
std::cout << "Recursive call!" << std::endl;
#endif

std::cout << t;

/**
* C++11 also offers a new use of the sizeof
* keyword. Coupled with a ..., it can return
* the number of arguments that are part
* of the typename coming in for Inputs! I
* thought this was hip [2].
*/
if (sizeof...(Inputs))
{
/**
* Make the recursive call. If you
* don't use the ... after parameters, you'll
* get an error:
*
* error: expression contains unexpanded parameter pack
*
* I'm going to write more about this in
* the conclusion, but without the ellipses that
* is what you will get.
*/
print(parameters...);
}

mtx.unlock();
}

void newThread(int id)
{
for(int i=0; i<5; ++i)
{
print("Thread #", id, " is in loop ", i, "\n");

boost::posix_time::milliseconds pauseTime(rand() % 1000);
boost::this_thread::sleep(pauseTime);
}
}


int main(int argc, char** argv)
{
boost::thread t1(newThread, 1);
boost::thread t2(newThread, 2);
boost::thread t3(newThread, 3);
boost::thread t4(newThread, 4);

t1.join();
t2.join();
t3.join();
t4.join();
}

/**
 * This entry turned out better than I thought it
 * would. I think there may be a slight performance hit
 * that may be unreasonable to some due to the recursive
 * nature of the print statement. It would be faster
 * if there was some sort of static unrolling (I have
 * nothing to back this up).
 *
 * So the elipses operator when used after a parameter
 * must perform some sort of operation similar to a
 * dereference operation. You can think of the
 * parameters object inside of the print function
 * as something akin to a pointer, except that it
 * is a pointer to a bunch of template parameters. To
 * get those parameters and expand you can use the
 * ellipses after it to "dereference" those parameters
 * and pass them along. The only fancy thing I see the
 * compiler doing is separating the first argument
 * from the variadic template argument and deducing
 * what function to call.
 */


/**

REFERENCES

[1] - http://insanecoding.blogspot.com/2010/03/c-201x-variadic-templates.html

[2] - http://www.cplusplus.com/articles/EhvU7k9E/

*/

No comments:

Post a Comment