Mar 24, 2012

[C++11][NOTE] Variadic Templates , Parameter Packs

template<typename… Types> // declare liststruct
Count; // walking template

template<typename T, typename… Rest> // walk list
struct Count<T, Rest…>
{
const static int value = Count<Rest…>::value +1;
};

template<> struct Count<> // recognize end of
{ // list
const static int value = 0;
};

auto count1 = Count<int, double, char>::value; // count1 = 3
auto count2 = Count<>::value; // count2 = 0





Count purely an exercise; C++0x’s sizeof…does the same thing:

template<typename… Types>
struct VerifyCount
{
static_assert(Count<Types…>::value == sizeof…(Types),
"Count<T>::value != sizeof…(T)");
};
Unpack (…) and sizeof…only two operations for parameter packs.


void print() { std::cout << '\n'; }; // print 0 objects
template<typename T, // type of 1st object
typename... TRest> // types of the rest
void print( const T& obj, // 1st object
const TRest&... rest) // the rest of them
{
std::cout << obj << " "; // print 1st object
print(rest...); // print the rest
}

double p = 3.14;
std::string s("Vari");
print(-22, p, &p, s, "adic"); // -22 3.14 0x22ff40 Vari adic

Unpacking Patterns:

Unpacking uses the pattern of the expression being unpacked:
template<class T, class... Args>
shared_ptr<T> // add “&&” to each
make_shared(Args&&... params); // unpacked elem.
template<typename T, typename... TRest> // add “const” and
void print(const T& obj, const TRest&... rest) // “&” to each
{
std::cout << obj << " ";
print(rest...); // add nothing to
} // each elem’s name
Call to print expands to:
print(rest1, rest2, ..., restn);
The ellipsis is always at the end of the pattern.

template<typename T> // return a normalized
T normalize(T&& obj); // copy of obj
template<typename F, typename… PTypes>
void normalizedCall( F func,
PTypes&&… params) // as before
{
func(normalize(params)…); // call normalize on
} // each unpacked elem
Call to func expands to:
func(normalize(params1), normalize(params2), ..., normalize(paramsn));





template <typename... Ts>
class C
{
: : :
};

template <typename... Ts>
void fun(const Ts&... vs)
{
: : :
}

Ts is not a type; vs is not a value!
typedef Ts MyList; // error!
Ts var; // error!
auto copy = vs; // error!

• Ts is an alias for a list of types
• vs is an alias for a list of values
• Either list may be potentially empty
• Both obey only specific actions


• Apply sizeof... to it, this will return how many class type in Ts, e.g , number of Arguments
size_t items = sizeof...(Ts); // or vs
• Expand back
template <typename... Ts>
void fun(Ts&&... vs) {
gun(3.14, std::forward<Ts>(vs)..., 6.28);
}

Expansion rules:

Be aware! ... is not , operator!
But expanded to a argument list!
http://stackoverflow.com/a/16012355
Thus:
#include <iostream>

using namespace std;

template<typename... T>
void layer(T... t)
{}

int run(int a)
{
    return a;
}


template<int... i>
void fun()
{
  /*run(i)...; // This won't work!*/
  layer(run(i)...); // This works!
}

int main()
{
    fun<1,2,3>();
}

• Initializer lists
any a[] = { vs... };
• Base specifiers
template <typename... Ts>
struct C : Ts... {};
template <typename... Ts>
struct D : Box<ts>... { : : : };
• Member initializer lists
// Inside struct D
template <typename... Us>
D(Us... vs) : Box<ts>(vs)... {}


• Template argument lists
std::map<Ts...> m;
• Exception specifications
◦ On second thought, scratch that
• Attribute lists
struct [[ Ts... ]] IAmFromTheFuture {};
• Capture lists
template <class... Ts> void fun(Ts... vs) {
auto g = [&vs...] { return gun(vs...); }
g();
}


CRAZY!!
Multiple expansions
• Expansion proceeds outwards
• These are different expansions!
template <class... Ts> void fun(Ts... vs) {
gun(A<Ts...>::hun(vs)...);
gun(A<Ts...>::hun(vs...));
gun(A<Ts>::hun(vs)...);
}

template <
typename T,
template <
template<class...> class... Policies
>
>
class ICantBelieveItsNotButter; 

---------------

How to use variadics?

• Pattern matching!
template <class T1, class T2>
bool isOneOf(T1&& a, T2&& b) {
return a == b;
}
template <class T1, class T2, class... Ts>
bool isOneOf(T1&& a, T2&& b, Ts&&... vs) {
return a == b || isOneOf(a, vs...);
}
assert(isOneOf(1, 2, 3.5, 4, 1, 2));

template <class T>
typename enable_if<is_integral<T>::value, long>::type
normalizeArg(T arg) { return arg; }
template <class T> typename
enable_if<is_floating_point<T>::value, double>::type
normalizeArg(T arg) { return arg; }
template <class T>
typename enable_if<is_pointer<T>::value, T>::type
normalizeArg(T arg) { return arg; }
const char* normalizeArg(const string& arg) {
return arg.c_str();
}

// Not really safe yet
template <typename... Ts>
int safe_printf(const char * f,
const Ts&... ts) {
return printf(f, normalizeArg(ts)...);
}


void check_printf(const char * f) {
for (; *f; ++f) {
if (*f != ’%’ || *++f == ’%’) continue;
throw Exc("Bad format");
}
}



template <class T, typename... Ts>
void check_printf(const char * f, const T& t,
const Ts&... ts) {
for (; *f; ++f) {
if (*f != ’%’ || *++f == ’%’) continue;
switch (*f) {
default: throw Exc("Invalid format char: %", *f);
case ’f’: case ’g’:
ENFORCE(is_floating_point<T>::value);
break;
case ’s’: . . .
}
return check_printf(++f, ts...); // AHA!!!
}
throw Exc("Too few format specifiers.");
}


template <typename... Ts>
int safe_printf(const char * f,
const Ts&... ts) {
check_printf(f, normalizeArg(ts)...);
return printf(f, normalizeArg(ts)...);
}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.