Monday, July 19, 2010

Unpacking tuples for function arguments

Sometimes you have a tuple and want to pass its contents to a function as arguments. One way is to overload the function:

int average(int a, int b, int c)
{
  return (a + b + c) / 3;
}

int average(std::tuple<int,int,int> x)
{
  return average(std::get<0>(x), std::get<1>(x), std::get<2>(x));
}

This works for an individual function or two, but it isn't a general solution and it doesn't scale. So let's automate it.

#include <functional> 
#include <tuple> 

namespace detail { 

template<std::size_t... Indices>
struct indices { typedef indices<Indices...,sizeof...(Indices)> next; }; 

// Generates the type indices<0,1,...,N-1> which is used to unpack an N-tuple.
template<std::size_t N>
struct make_indices { typedef typename make_indices<N-1>::type::next type; };

template<>
struct make_indices<0> { typedef indices<> type; };

// We use the indices to unpack the tuple.
template<class Function, class... T, std::size_t... Indices> 
inline typename std::result_of<Function(T...)>::type 
apply(Function f, std::tuple<T...>&& x, indices<Indices...>) 
{ 
  return f(std::forward<T>(std::get<Indices>(x))...); 
} 

} // namespace detail 

// Apply function f to the contents of tuple x.
template<class Function, class... T> 
inline typename std::result_of<Function(T...)>::type 
apply(Function f, std::tuple<T...>&& x) 
{ 
  typedef typename detail::make_indices<sizeof...(T)>::type Indices; 
  return detail::apply(f, std::forward<std::tuple<T...>>(x), Indices()); 
}

Now we can call our functions this way:

int average(int a, int b, int c) 
{ 
  return (a + b + c) / 3; 
} 

int zero() 
{ 
  return 0; 
} 

int main() 
{ 
  std::cout << apply(average, std::make_tuple(1, 2, 3)) << '\n'; 

  std::cout << apply(zero, std::make_tuple()) << '\n'; 

  std::cout << apply([](int x, int y){ return x + y; }, std::make_tuple(2, 3)) << '\n'; 
}

Thursday, March 11, 2010

Curious

In designs that use the curiously recurring template pattern, the base class usually needs access to the derived class. Typically this is done by casting the base class's this pointer. This technique is encapsulated in the curious class below. Simply derive the base class from curious to inherit a derived() member function that returns a pointer to the derived class.

template<class Derived>
class curious {
protected:

  typedef Derived derived_type;

  derived_type* derived()
  {
    return static_cast<derived_type*>(this);
  }

  derived_type const* derived() const
  {
    return static_cast<derived_type const*>(this);
  }
};

Tuesday, March 9, 2010

Compile-time logic

Sometimes it is helpful to compute the result of logical operations like and and or at compile-time. These operations return a boolean result, so the first thing a compile-time logic library needs is classes to represent true and false results. Here is one way to do it.

template<bool c>
struct bool_ {

  typedef bool_ type;

  typedef bool value_type;

  static bool const value = c;

  operator bool() const { return c; }
};

typedef bool_<true> true_;

typedef bool_<false> false_;

It would also have been possible to simply write

struct true_ { };

struct false_ { };

but the nested typedefs and bool will be helpful later on. The implicit conversion to bool is occasionally helpful but code that uses it can always be rewritten to make it unnecessary, for instance by using tag dispatching.

Before we continue with the presentation of the code, let's describe the algorithms we are writing. In the case of the short-circuit and, it is this: if the first argument is false, then the result is false; otherwise it is the result of applying short-circuit and to the remaining arguments. If there is only one argument, then the result is the same as the argument. The short-circuit or is similar: if the first argument is true, then the result is true; otherwise it is the result of applying short-circuit or to the remaining arguments. If there is only one argument, then the result is the same as the argument. Note that as stated these algorithms apply even to the logical and or or of a single argument, but for consistency with the built-in && and || operators we will require at least two arguments.

It should be clear from the statement of the algorithms that we need some means of branching, that is to say some means of representing an if-then-else construct. This is accomplished by the if_ class. if_ has three template parameters: a boolean condition and two types. If the condition is true, then if_ selects the first type; otherwise it selects the second type.

template<bool c, class T1, class T2>
struct if_c { typedef T1 type; };

template<class T1, class T2>
struct if_c<false,T1,T2> { typedef T2 type; };

template<class C, class T1, class T2>
struct if_ : if_c<C::value,T1,T2> { };

There are two versions of if_. The first is called if_c and uses a bool template parameter for its condition. The second is called if_ and uses a class with a nested value for its condition; because value is passed to if_c, it must be a type convertible to bool at compile-time.

Now we understand the purpose of the value nested in bool_. It allows that class to be used with if_ as a condition.

We are nearing the finish line now, but before we continue let's step back from the details of the code and take a high-level look at what we intend to achieve. We are going to write a class named and_ that takes two or more template arguments, computes the short-circuit logical and of the arguments, and exposes the result as a nested type named type that will be a typedef for either true_ or false_.

We already described the algorithm that computes the result; now we describe its implementation: if the first argument to and_ is false, then and_ inherits false_; otherwise it inherits the result of applying and_ to the remainder of its arguments. Now we understand the purpose of the type type nested in bool_: when and_ inherits false_ (or true_, as the case may be), it also inherits the appropriate nested type.

We intend to make and_ inherit from one class or another based on a condition, but this is useful behavior in its own right so let's encapsulate it in a class named eval_if. Like if_, eval_if takes a boolean condition and two types as arguments. If the condition is true, then eval_if inherits the first type; otherwise it inherits the second type. As with if_, there are two versions of eval_if: one that takes a bool condition, and one that takes a class with a nested value.

template<bool c, class F1, class F2>
struct eval_if_c : if_c<c,F1,F2>::type { };

template<class C, class F1, class F2>
struct eval_if : if_<C,F1,F2>::type { };

Now we are ready to implement and_.

template<class... F>
struct and_;

template<class F1, class F2, class... F>
struct and_<F1,F2,F...> : eval_if<F1,and_<F2,F...>,false_>::type { };

template<class F1, class F2>
struct and_<F1,F2> : eval_if<F1,eval_if<F2,true_,false_>,false_>::type { };

Let's examine the two specializations of and_ one at a time. The critical bit of code in the first is eval_if<F1,and_<F2,F...>,false_>::type. This names a type. If F1 is false, then the type is false_; otherwise it is and_<F2,F...>. Thus if the first argument to and_ is false, then it inherits false_; otherwise it recurses and inherits and_<F2,F...>.

The second specialization ends the recursing of the first when and_ is evaluating exactly two arguments. If the first argument to and_ is false, then this specialization inherits false_; otherwise it inherits eval_if<F2,true_,false_>, which in turn inherits either either true_ or false_ depending on F2.

The implementation of logical or is similar.

template<class... F>
struct or_;

template<class F1, class F2, class... F>
struct or_<F1,F2,F...> : eval_if<F1,true_,or_<F2,F...>>::type { };

template<class F1, class F2>
struct or_<F1,F2> : eval_if<F1,true_,eval_if<F2,true_,false_>>::type { };

The class names used here are borrowed from the the Boost Metaprogramming Library, which inspired this post. The implementation is my own.