/**
* 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/
*/
Saturday, May 11, 2013
Variadic Templates are Hip.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment