Related
I have a family of classes with methods with the following signature:
double compute(list<T> pars)
This method performs a calculation with the parameters received through pars. For each compute(list) method, I have another compute(x1, x2, ..., xn) which is the method implementing the real calculation. Thus, compute(pars) should do some such as:
double compute(list<T> pars)
{
T x1 = list.pop_back();
T x2 = list.pop_back();
// .. so on until last parameter xn
T xn = list.pop_back();
return compute(x1, x2, .., xn); // here the real implementation is called
}
This pattern repeats many times, the only thing that could change is the size of pars list and of course the implementation of compute(x1, x1, ..).
I would like to find a way for "driying" this repetitive process; concretely, extracting the parameters in pars list and building the call to compute(x1, x2, .., xn). I have been trying without success to do some macro tricks.
My question is if it exists some way based on metaprogramming that allows me to implement compute(list<T> pars) once and simply reuse it n order to perform the call to compute(x1, x2, ..., xn)
EDIT: This is the signature of the other compute(x1, ...)
VtlQuantity compute(const VtlQuantity & x1,
const VtlQuantity & x2,
// any number of pars according the class
const VtlQuantity & xn) const
'VtlQuantityis a class representingdouble`'s, their units and other stuff.
You may do the following:
template <typename Func, typename T, std::size_t ... Is>
decltype(auto) apply(Func&& f, const std::list<T>& pars, std::index_sequence<Is...>)
{
std::vector<T> v(pars.rbegin(), pars.rend());
return std::forward<Func>(f)(v.at(Is)...);
}
template <std::size_t N, typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
With usage similar to:
apply<6>(print, l);
Demo
To compute automatically the arity of the function, you may create a traits:
template <typename F> struct arity;
template <typename Ret, typename ...Args> struct arity<Ret(Args...)>
{
static constexpr std::size_t value = sizeof...(Args);
};
and then
template <typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
constexpr std::size_t N = arity<std::remove_pointer_t<std::decay_t<Func>>>::value;
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
Demo
You have to enrich arity to support Functor (as the lambda).
This is a C++11 solution for the more general problem type of applying
a function or functor F, taking N type T parameters and returning type Ret, to the N arguments
at successive positions of some input iterator.
This gains several flexibilities over a solution parameterized by some container-of-T of the arguments:-
You can extract the arguments from an arbitrary N-sized range within a sequence.
The sequence need not be a container-of-T - though it must be a sequence of something convertible to T.
You can extract the arguments either last-to-first (as you do), or first-to-last,
from the standard container types or any that support forward and reverse iterators.
You may even apply F to arguments consumed directly from some input stream, without
intermediate extraction.
And of course you can change your mind about the type of sequence in which
to deliver arguments without having to change the functional-application solution.
Interface
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop());
You can use this like:
auto result = invoke(func,iter);
to apply func to the arguments at N successive positions of the iterator
iter.
That way, you get no range-checking that N arguments are legitimately accessible
to your program at those positions. The range-checking code that you will spot
in the implementation will compile to nothing and if you trespass out of bounds
there will be UB.
If you want range checking you can instead code:
auto result = invoke(func,iter,end);
where end is an iterator of the same type as iter delimiting the end of the
available range in the usual manner. In this case an std::out_of_range will
be thrown if N exceeds the size of the range.
Implementation
#include <type_traits>
#include <functional>
#include <string>
template<typename T>
struct function_traits;
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<Ret(*)(ArgT, ArgRest...)>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<std::function<Ret(ArgT, ArgRest...)>>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
namespace detail {
template<typename Left, typename Right>
typename std::enable_if<!std::is_same<Left,Right>::value>::type
range_check(Left, Right, std::string const &){}
template<typename Left, typename Right>
typename std::enable_if<std::is_same<Left,Right>::value>::type
range_check(Left start, Right end, std::string const & gripe) {
if (start == end) {
throw std::out_of_range(gripe);
}
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N == function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter, Stop, Ts...args)
{
return f(args...);
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N != function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter it, Stop stop, Ts...args)
{
range_check(it,stop,
"Function takes more arguments than are available "
"in `" + std::string(__PRETTY_FUNCTION__) + '`');
using arg_type = typename
function_traits<typename std::decay<Func>::type>::first_arg_type;
auto arg = static_cast<arg_type>(*it);
return invoke<N + 1>(std::forward<Func>(f),++it,stop,args...,arg);
}
} // namespace detail
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop())
{
return detail::invoke<0>(std::forward<Func>(f),it,stop);
}
The two specializations of function_traits<T> provided will restrict
compilation to functional types T that take at least one argument, which should
suffice for likely applications. Should you need to support
invocation on types taking 0 arguments then you can augment them with:
template <typename Ret>
struct function_traits<Ret(*)()>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
template <typename Ret>
struct function_traits<std::function<Ret()>>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
The specialization for free functions function_traits<Ret(*)(ArgT, ArgRest...)>,
is strictly a redundant convenience, since they too could be wrapped in std::function
objects, as you're obliged to do for anything fancier than a free function.
Demo
For a program that exercises the features discussed you can append:
#include <iostream>
#include <list>
#include <vector>
#include <deque>
#include <sstream>
#include <iterator>
struct num
{
double d;
explicit operator double() const {
return d;
}
};
double add4(double d0, double d1, double d2, double d3)
{
std::cout << d0 << '+' << d1 << '+' << d2 << '+' << d3 << "\n=";
return d0 + d1 + d2 + d3;
}
int multiply2(int i0, int i1)
{
std::cout << i0 << '*' << i1 << "\n=";
return i0 * i1;
}
struct S
{
int subtract3(int i0, int i1, int i2) const
{
std::cout << i0 << '-' << i1 << '-' << i2 << "\n=";
return i0 - i1 - i2;
}
int compute(std::list<int> const & li) const {
std::function<int(int,int,int)> bind = [this](int i0, int i1, int i2) {
return this->subtract3(i0,i1,i2);
};
return invoke(bind,li.begin());
}
};
int main()
{
std::vector<double> vd{1.0,2.0,3.0,4.0};
std::vector<double> vdshort{9.0};
std::list<int> li{5,6,7,8};
std::deque<num> dn{num{10.0},num{20.0},num{30.0},num{40.0}};
std::istringstream iss{std::string{"10 9 8"}};
std::istream_iterator<int> it(iss);
std::cout << invoke(add4,vd.rbegin()) << '\n';
std::cout << invoke(multiply2,li.begin()) << '\n';
std::cout << invoke(add4,dn.rbegin()) << '\n';
std::cout << invoke(multiply2,++it) << '\n';
S s;
std::cout << '=' << s.compute(li) << '\n';
try {
std::cout << invoke(add4,vdshort.begin(),vdshort.end()) << '\n';
} catch(std::out_of_range const & gripe) {
std::cout << "Oops :(\n" << gripe.what() << '\n';
}
return 0;
}
The case of:
S s;
std::cout << '=' << s.compute(li) << '\n';
is particularly pertinent to your particular problem, since here we call
S::compute(std::list<int> const & li) to apply another non-static method
of S to arguments delivered in the list li. See in the implementation
of S::compute how the use of a lambda can conveniently bind both the
calling S object and S::compute into an std::function we can
pass to invoke.
Live demo
C++17 solution below. wandbox link
(Greatly simplified thanks to Jarod42)
Assumes the number of arguments N is known at compile-time, but the list can have any size.
This calls pop_back() multiple times as shown in the example, then calls a function.
template <typename T>
struct list
{
T pop_back() { return T{}; }
};
namespace impl
{
template<typename TList, std::size_t... TIs>
auto list_to_tuple(TList& l, std::index_sequence<TIs...>)
{
using my_tuple = decltype(std::make_tuple((TIs, l.pop_back())...));
return my_tuple{((void)TIs, l.pop_back())...};
}
}
template<std::size_t TN, typename TList>
auto list_to_tuple(TList& l)
{
return impl::list_to_tuple(l, std::make_index_sequence<TN>());
}
template <std::size_t TN, typename TList, typename TF>
auto call_with_list(TList& l, TF&& f)
{
return std::experimental::apply(f, list_to_tuple<TN>(l));
}
void test_compute(int, int, int)
{
// ...
}
int main()
{
list<int> l{};
call_with_list<3>(l, test_compute);
}
How does it work?
The idea is that we "convert" a list to a tuple, specifying how many elements we want to pop from the list at compile-time using list_to_tuple<N>(list).
After getting a tuple from the list, we can use std::experimental::apply to call a function by applying the elements of the tuple as arguments: this is done by call_with_list<N>(list, func).
To create a tuple from the list, two things needs to be done:
Creating an std::tuple<T, T, T, T, ...>, where T is repeated N times.
Call list<T>::pop_back() N times, putting the items in the tuple.
To solve the first problem, decltype is used to get the type of the following variadic expansion: std::make_tuple((TIs, l.pop_back())...). The comma operator is used so that TIs, l.pop_back() evaluates to decltype(l.pop_back()).
To solve the second problem, a variadic expansion is used inside the std::initializer_list tuple constructor, which guarantees order-of-evaluation: return my_tuple{((void)TIs, l.pop_back())...};. The same comma operator "trick" described above is used here.
Can I write it in C++11?
Yes, but it will be slightly more "annoying".
std::experimental::apply is not available: look online for solutions like this one.
std::index_sequence is not available: you will have to implement your own.
template<class T> using void_t = void;
template<class T, class F, std::size_t N=0, class=void>
struct arity:arity<T, F, N+1> {};
template<class F, class T, class Indexes>
struct nary_result_of{};
template<std::size_t, class T>
using ith_T=T;
template<class F, class T, std::size_t...Is>
struct nary_result_of<F, T, std::index_sequence<Is...>>:
std::result_of<F( ith_T<Is, T> )>
{};
template<class T, class F, std::size_t N>
struct arity<T, F, N, void_t<
typename nary_result_of<F, T, std::make_index_sequence<N>>::type
>>:
std::integral_constant<std::size_t, N>
{};
arity uses one C++14 feature (index sequences, easy to write in C++11).
It takes types F and a T and tells you the least number of Ts you can pass to F to make the call valid. If no number of T qualify, it blows your template instantiation stack and your compiler complains or dies.
template<class T>
using strip = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
namespace details {
template<class T, std::size_t N, class F, class R,
std::size_t...Is
>
auto compute( std::index_sequence<Is...>, F&& f, R&& r ) {
std::array<T, N> buff={{
(void(Is), r.pop_back())...
}};
return std::forward<F>(f)( buff[Is]... );
}
}
template<class F, class R,
class T=strip< decltype( *std::declval<R&>().begin() ) >
>
auto compute( F&& f, R&& r ) {
return details::compute( std::make_index_sequence<arity<F,T>{}>{}, std::forward<F>(f), std::forward<R>(r) );
}
The only thing really annoying to convert to C++11 is the auto return type on compute. I'd have to rewrite my arity.
This version should auto detect the arity of even non-function pointers, letting you call this with lambdas or std::functions or what have you.
I've made a function that calculates the sine of a number. It returns the input type if it is std::is_floating_point. But for std::is_integral, it returns a double.
template<class T , typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
double mysin(const T& t) // note, function signature is unmodified
{
double a = t;
return std::sin(a);
}
template<class T , typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr >
T mysin(const T& t) // note, function signature is unmodified
{
return std::sin(t);
}
Easy enough. Now I'd like this to work for vectors (or arrays) and tuples (or clusters). So that:
(pseudo code:)
std::vector<std::double> a = mysin(std::vector<std::int>);
std::tuple<std::double, std::float> b = mysin(std::tuple<std::int, std::float>);
std::vector<std::tuple<std::double, std::float>> c = mysin(std::vector<std::tuple<std::int, std::float>>);
std::tuple<std::vector<std::double>, std::float> d = mysin(std::tuple<std::vector<std::int>, std::float>);
std::tuple<std::tuple<std::double, std::vector<std::double>>, std::float>> e = mysin(std::tuple<std::tuple<std::int, std::vector<std::int>>, std::float>>);
and so on...
In most examples about tuple templates, the function either has no return value, returns an accumulated value, or has the same return type as the input.
I've experimented a lot with these topics (among others):
Traversing nested C++11 tuple , c++11: building a std::tuple from a template function , How to make a function that zips two tuples in C++11 (STL)?
The last one has been especially useful. I've got this working for tuples, but not for recursive tuples (tuples in tuples).
Eventually (if this is possible at all), I'll have to make a mycos, mytan, myasin, etc. Using gcc 4.9.2.
**EDIT:**So this is what I came up with after Yakk's suggestions, and a little tweaking:
#include <utility>
#include <vector>
#include <memory>
#include <typeinfo> // used for typeid
#include <tuple>
#include <cstdlib> // for math functions?
#include <cmath> // for math functions
#include <type_traits> // for std::enable_if
template<class T , typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
double mysin(const T& t) { // note, function signature is unmodified
double a = t;
return std::sin(a);
// printing a debug string here will
// print tuple elements reversed!!
}
template<class T , typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr >
T mysin(const T& t) {// note, function signature is unmodified
// printing a debug string here will
// print tuple elements reversed!!
return std::sin(t);
}
struct sine_t {
template<class T>
auto operator()(T&&t)const->
decltype(mysin(std::declval<T>())) {
return mysin(std::forward<T>(t));
}
};
template<class F>
struct vectorize {
template<class T,
class R=std::vector< std::result_of_t< vectorize<F>(T const&) > >
>
R operator()( std::vector<T> const& v ) const {
R ret;
ret.reserve(v.size());
for( auto const& e : v ) {
ret.push_back( vectorize<F>{}(e) );
}
return ret;
}
template<
class X,
class R=std::result_of_t< F(X const&) >
>
R operator()( X const& x ) const {
return F{}(x);
}
template<
class R,
class... Ts,
size_t... Is
>
R tup_help( std::index_sequence<Is...>, std::tuple<Ts...> const& t ) const {
return std::make_tuple( vectorize<F>{}(std::get<Is>(t))... );
}
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const {
return tup_help<R>( std::index_sequence_for<Ts...>{}, t );
}
};
//+++++++++++++++++++++++++++++++++++++++++
int main() {
std::vector<int> a = {1 ,2};
std::tuple<int, double, int, double> b (42, -3.14, 42, -3.14);
auto c = vectorize<sine_t>()(a);
auto d = vectorize<sine_t>()(b);
std::vector<std::tuple<int, int> > e {std::make_tuple(1 ,2)};
//This does not not work:
//auto f = vectorize<sine_t>()(e);
//This works:
std::tuple<std::vector<int> > g ( a );
auto f = vectorize<sine_t>()(g);
return 0;
}
This works. Needs c++14.
First an overload set object. This is useful because it lets us pass around the entire overload set as a single object:
struct sine_t {
template<class T>
auto operator()(T&&t)const->
decltype(mysin(std::declval<T>()))
{ return mysin(std::forward<T>(t)); }
};
next, we want to "vectorize" a given function object.
We'll start simple:
template<class F>
struct vectorize {
template<class T, class R=std::vector< std::result_of_t< F(T const&) > >>
R operator()( std::vector<T> const& v ) const {
R ret;
ret.reserve(v.size());
for( auto const& e : v ) {
ret.push_back( F{}(e) );
}
return ret;
}
template<class X, class R=std::result_of_t< F(X const&) >>
R operator()( X const& x ) const {
return F{}(x);
}
};
this supports 1 level recursion, and only on std::vector.
To allow infinite recursion of nested std::vectors, we modify the operator() overload for std::vector:
template<
class T,
class R=std::vector< std::result_of_t< vectorize<F>(T const&) > >
>
R operator()( std::vector<T> const& v ) const {
R ret;
ret.reserve(v.size());
for( auto const& e : v ) {
ret.push_back( vectorize<F>{}(e) );
}
return ret;
}
and now we support std::vector<std::vector<int>>.
For tuple support, we add 2 functions. The first one has this signature:
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const
which gets us our return value (half the battle). To actually do the mapping, use the indexes trick:
template<
class R,
class... Ts,
size_t... Is
>
R tup_help( std::index_sequence<Is...>, std::tuple<Ts...> const& t ) const
{
return std::make_tuple( vectorize<F>{}(std::get<Is>(t))... );
}
template<
class... Ts,
class R=std::tuple< std::result_of_t< vectorize<F>(Ts const&) >... >
>
R operator()( std::tuple<Ts...> const& t ) const {
return tup_help<R>( std::index_sequence_for<Ts...>{}, t );
}
similar code for std::array and raw C arrays should work (converting the raw C array to a std::array naturally).
std::index_sequence etc is C++14, but easy to write a version that supports 100s of elements in C++11. (A version that supports large arrays takes more work). The result_of_t alias (and any similar) are also C++14, but the aliases are easy to write in C++11, or you can just typename std::result_of<?>::type verbose explosion.
In the end, you vectorize<sine_t>{} and pass in whatever.
If you want a function instead of a function object, simply have it delegate the work to vectorize<sine_t>.
I would like to use templates for optimization as described here. However, with a growing number of bool template arguments, instantiating the template might have too many branches. And it gets even more branchy if you use larger enums instead of bools.
#include <iostream>
using namespace std;
template <bool b1, bool b2>
int HeavyLoop_impl(int arg)
{
for (int i = 0; i < 10000000; i++)
{
// b1 is known at compile-time, so this branch will be eliminated
if (b1) { arg += 1; }
else { arg += 2; }
// b2 is known at compile-time, so this branch will be eliminated
if (b2) { arg += 10; }
else { arg += 20; }
}
return arg;
}
// This function could be generated automatically
void HeavyLoop(bool b1, bool b2, int arg)
{
int res;
if (b1) {
if (b2) { res = HeavyLoop_impl<true, true>(arg); }
else { res = HeavyLoop_impl<true, false>(arg); }
} else {
if (b2) { res = HeavyLoop_impl<false, true>(arg); }
else { res = HeavyLoop_impl<false, false>(arg); }
}
cout << "res: "<<res<<endl;
}
int main(int argc, char**argv)
{
bool b1 = true;
bool b2 = false;
int arg = 0;
HeavyLoop(b1, b2, arg);
return 0;
}
Is there any way to automatically generate the HeavyLoop function? I would like something like this:
vars_to_template_function<bool, bool>(HeavyLoop_impl, b1, b2, arg);
Would that be possible somehow? Thanks for any hints.
Note: this is only a very simplified example. The actual loop is is of course more complicated :o)
I decided to have some more fun with the code, here's an improved version over my first attempt which has the following benefits:
Supports enum types
Explicitly specify how many parameters should be converted
Generic implementation for the complicated part, one small helper for each function that uses it.
The code:
#include <iostream>
#include <utility>
#include <type_traits>
// an enum we would like to support
enum class tribool { FALSE, TRUE, FILE_NOT_FOUND };
// declare basic generic template
// (independent of a specific function you'd like to call)
template< template< class > class CB, std::size_t N, typename = std::tuple<> >
struct var_to_template;
// register types that should be supported
template< template< class > class CB, std::size_t N, typename... Cs >
struct var_to_template< CB, N, std::tuple< Cs... > >
{
// bool is pretty simple, there are only two values
template< typename R, typename... Args >
static R impl( bool b, Args&&... args )
{
return b
? var_to_template< CB, N-1, std::tuple< Cs..., std::true_type > >::template impl< R >( std::forward< Args >( args )... )
: var_to_template< CB, N-1, std::tuple< Cs..., std::false_type > >::template impl< R >( std::forward< Args >( args )... );
}
// for each enum, you need to register all its values
template< typename R, typename... Args >
static R impl( tribool tb, Args&&... args )
{
switch( tb ) {
case tribool::FALSE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FALSE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::TRUE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::TRUE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::FILE_NOT_FOUND:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FILE_NOT_FOUND > > >::template impl< R >( std::forward< Args >( args )... );
}
throw "unreachable";
}
// in theory you could also add int, long, ... but
// you'd have to switch on every possible value that you want to support!
};
// terminate the recursion
template< template< class > class CB, typename... Cs >
struct var_to_template< CB, 0, std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return CB< std::tuple< Cs... > >::template impl< R >( std::forward< Args >( args )... );
}
};
// here's your function with the template parameters
template< bool B, tribool TB >
int HeavyLoop_impl( int arg )
{
for( int i = 0; i < 10000000; i++ ) {
arg += B ? 1 : 2;
arg += ( TB == tribool::TRUE ) ? 10 : ( TB == tribool::FALSE ) ? 20 : 30;
}
return arg;
}
// a helper class, required once per function that you'd like to forward
template< typename > struct HeavyLoop_callback;
template< typename... Cs >
struct HeavyLoop_callback< std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return HeavyLoop_impl< Cs::value... >( std::forward< Args >( args )... );
}
};
// and here, everything comes together:
int HeavyLoop( bool b, tribool tb, int arg )
{
// you provide the helper and the number of arguments
// that should be converted to var_to_template<>
// and you provide the return type to impl<>
return var_to_template< HeavyLoop_callback, 2 >::impl< int >( b, tb, arg );
}
int main()
{
bool b = true;
tribool tb = tribool::FALSE;
int arg = 0;
int res = HeavyLoop( b, tb, arg );
std::cout << "res: " << res << std::endl;
return 0;
}
And here's a live example in case you want to play with it.
Here's how you can do it:
#include <iostream>
using namespace std;
template <bool b1, bool b2>
struct HeavyLoopImpl
{
static int func(int arg)
{
for (int i = 0; i < 10000000; i++) {
arg += b1 ? 1 : 2;
arg += b2 ? 10 : 20;
}
return arg;
}
};
template <template<bool...> class Impl,bool...Bs>
struct GenericJump
{
template<typename... Args>
static int impl(Args&&... args)
{
return Impl<Bs...>::func(std::forward<Args>(args)...);
}
template<typename... Args>
static int impl(bool b, Args&&... args)
{
return b
? GenericJump<Impl,Bs...,true >::impl(std::forward<Args>(args)...)
: GenericJump<Impl,Bs...,false>::impl(std::forward<Args>(args)...);
}
};
int HeavyLoop(bool b1, bool b2, int arg)
{
return GenericJump<HeavyLoopImpl>::impl(b1,b2,arg);
}
int main()
{
bool b1 = true;
bool b2 = false;
int arg = 0;
int res = HeavyLoop(b1, b2, arg);
cout << "res: "<<res<<endl;
return 0;
}
This is basically Daniels solution, but it allows you to use functions other than HeavyLoop_impl() as implementation. Only being able to call a single template function kind of defeats the purpose of being a generic solution. The GenericJump template class can call other functions also. You only have to change the HeavyLoop_impl() template function into a template class with a static function func(). It works marvellously. It compiles with gcc 4.7.3 and gives the correct output.
The ideal generic solution really depends on what you want to vary. The possibilities for variation are:
The number of branches, and the types of the variables being branched on.
The operation to be performed and the number and types of its arguments.
I would recommend not making an completely generic solution unless you really need to vary all of those things. Consider only the things you want to vary and your life will be much easier.
Assuming that the total number of branches is less than 2^64, you can use a switch statement to do the dispatch. The following solution demonstrates how this could work:
template<unsigned permutation>
struct Permutation
{
static_assert(permutation < 4, "permutation must be in the range [0, 4)");
static const bool b1 = permutation & (1 << 0);
static const bool b2 = permutation & (1 << 1);
};
unsigned makePermutation(bool b1, bool b2)
{
return (b1 << 0) | (b2 << 1);
}
template<unsigned p>
int HeavyLoop_impl(int arg)
{
return HeavyLoop_impl<Permutation<p>::b1, Permutation<p>::b2>(arg);
}
int HeavyLoop_impl(unsigned permutation, int arg)
{
switch(permutation)
{
case 0: return HeavyLoop_impl<0>(arg);
case 1: return HeavyLoop_impl<1>(arg);
case 2: return HeavyLoop_impl<2>(arg);
case 3: return HeavyLoop_impl<3>(arg);
}
}
[Note: It would be trivial to use Boost.Preprocessor to generate the above switch statement.]
void HeavyLoop(bool b1, bool b2, int arg)
{
int res = HeavyLoop_impl(makePermutation(b1, b2), arg);
cout << "res: "<<res<<endl;
}
I think the best answer to your question is actually not to generate it automatically and leave it how you already have it in the question.
Making an automatic template function to generate the middle ground just obfuscates the invariant switching you're doing in the first place.
I much prefer to try to understand how the middle layer works in your question, than any of the answers people have offered for you.
I have a similar example. In my case I can apply a number of different operations between an array of values. The arrays are equal size. However I also have a structure that maps sub-ranges of the array with weight values that affect my operations.
So for instance, I might be working with arrays of 100 values, and have ranges with weights like this:
[0,25] rangeWeight = 0
[26,35] rangeWeight = 0.25
[36,50] rangeWeight = 0.5
[51,99] rangeWeight = 1.0
So each operation looks something like (pseudo):
for each subrange:
alias to the dst buffer
alias to the src buffer
determine the number of elements in the range
if there's any
weight = weightPassedIn * rangeWeight;
Op(dst, src, weight, numElements);
For me, there were several optimizations involving whether or not the destination is touched or not (if it was still at the cleared value, some assumptions can be made to simplify the math per operation), Also if the weight happens to be full, 1.0, there are other shortcuts.
At first I was writing the same loop over and over with all of the same setup, and once I refactored all the loops around each op into functions, I pretty much naturally had the form of your invariant wrapper. There actually turned out to be several wrappers which mainly just wrap the high level operation happening inside the loop, and otherwise just handle the individual optimizations like this:
if (weight == 1.0f)
{
if ( arrayIsCleared )
Blend<BlendOpSet, true, false>(otherBuff, subRangesMask, 1.0f);
else
Blend<BlendOpAccumulate, true, false>(otherBuff, subRangesMask, 1.0f);
}
else
{
if ( arrayIsCleared )
Blend<BlendOpSet, false, false>(otherBuff, subRangesMask, weight);
else
Blend<BlendOpAccumulate, false, false>(otherBuff, subRangesMask, weight);
}
Have you considered passing a function as template argument? This way you can tailor your inner loop to your own wishes.
Function passed as template argument
However, the function call will have a small overhead.
Here is another solution with boost::hana, which can handle also enums:
#include <cstdio>
#include <type_traits>
#include <boost/hana.hpp>
namespace hana = boost::hana;
template <typename F, typename TArgs, typename TIn, typename TOut>
void fun_arg_combinations_impl(F&& f, TArgs targs, TIn tin, TOut tout) {
if constexpr (hana::is_empty(tin)) {
hana::unpack(tout, f);
} else {
hana::for_each(hana::front(tin), [&](auto v){
if (v == hana::front(targs)) {
fun_arg_combinations_impl(f, hana::drop_front(targs), hana::drop_front(tin), hana::append(tout, v));
}
});
}
}
template <typename F, typename TArgs, typename TIn>
void fun_arg_combinations(F&& f, TArgs targs, TIn tin) {
fun_arg_combinations_impl(f, targs, tin, hana::tuple<>());
}
enum Shape {LINE, CIRCLE, SQUARE};
int main()
{
auto f_heavy_loop = [](auto b1t, auto b2t, auto st) {
constexpr bool b1 = decltype(b1t)::value;
constexpr bool b2 = decltype(b2t)::value;
constexpr Shape s = decltype(st )::value;
printf("out:%d %d %d\n", b1, b2, s);
};
//constexpr auto bools = hana::make_tuple(std::true_type{}, std::false_type{});
constexpr auto bools = hana::tuple<std::true_type, std::false_type>{};
constexpr auto shapes = hana::tuple<
std::integral_constant<Shape, LINE>,
std::integral_constant<Shape, CIRCLE>,
std::integral_constant<Shape, SQUARE>>{};
// Using volatile to not allow the compiler to optimize for hard-coded values
volatile bool b1 = true;
volatile bool b2 = false;
volatile Shape s = SQUARE;
fun_arg_combinations(
f_heavy_loop,
hana::make_tuple(b1 , b2 , s ),
hana::make_tuple(bools, bools, shapes));
}
b1, b2 and s inside the f_heavy_loop() lambda are all constexpr, so we can use if constexpr on them.
output:
out:1 0 2
Have a look at the generated assembly here: https://godbolt.org/z/nsF2l5
Sometimes you want a function to return multiple values. One very common way to
achieve such a behavior in C++ is to pass your values by non-const reference and
assign to them in your function:
void foo(int & a, int & b)
{
a = 1; b = 2;
}
Which you would use:
int a, b;
foo(a, b);
// do something with a and b
Now I have a functor that accepts such a function and would want to forward the
set arguments into another function returning the result:
template <typename F, typename G>
struct calc;
template <
typename R, typename ... FArgs,
typename G
>
struct calc<R (FArgs...), G>
{
using f_type = R (*)(FArgs...);
using g_type = G *;
R operator()(f_type f, g_type g) const
{
// I would need to declare each type in FArgs
// dummy:
Args ... args;
// now use the multiple value returning function
g(args...);
// and pass the arguments on
return f(args...);
}
};
Does this approach even make sense, or should I rather use a tuple based
approach? Is there something smarter than a tuple based approach here?
You could use compile-time indices:
template< std::size_t... Ns >
struct indices
{
typedef indices< Ns..., sizeof...( Ns ) > next;
};
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;
};
template< typename F, typename G >
struct calc;
template<
typename R, typename ... FArgs,
typename G
>
struct calc< R (FArgs...), G >
{
using f_type = R (*)(FArgs...);
using g_type = G *;
private:
template< std::size_t... Ns >
R impl(f_type f, g_type g, indices< Ns... > ) const
{
std::tuple< FArgs ... > args;
g( std::get< Ns >( args )... );
// alternatively, if g() returns the tuple use:
// auto args = g();
return f( std::get< Ns >( args )... );
}
public:
R operator()(f_type f, g_type g) const
{
return impl( f, g, typename make_indices< sizeof...( FArgs ) >::type() );
}
};
When accepting the fact that we're changing the signature of both f and g to work with std::tuple, the answer to this problem becomes trivial:
template <typename F, typename G> struct calc;
template <typename R, typename ... Args>
struct calc<R (std::tuple<Args...> const &), std::tuple<Args...> ()>
{
using f_type = R (*)(std::tuple<Args...> const &);
using g_type = std::tuple<Args...> (*)();
R operator()(f_type f, g_type g) const
{
return f(g());
}
};
Here is a simple example:
int sum(std::tuple<int, int> const & t) { return std::get<0>(t) + std::get<1>(t); }
std::tuple<int, int> gen() { return std::make_tuple<int, int>(1, 2); }
auto x = calc<decltype(sum), decltype(gen)>()(&sum, &gen);
The limitation of this solution is clear however: you have to write your own functions. Using something like std::pow as f is not possible using this approach.
I recently ran across this puzzle, was finally able to struggle out a hacky answer (using index arrays), and wanted to share it (answer below). I am sure there are answers that use template recursion and answers that use boost; if you're interested, please share other ways to do this. I think having these all in one place may benefit others and be useful for learning some of the cool C++11 template metaprogramming tricks.
Problem:
Given two tuples of equal length:
auto tup1 = std::make_tuple(1, 'b', -10);
auto tup2 = std::make_tuple(2.5, 2, std::string("even strings?!"));
How do you create a function that will "zip" the two tuples into a heterogeneous tuple of pairs?
std::tuple<
std::pair<int, double>,
std::pair<char, int>,
std::pair<int, std::string> > result =
tuple_zip( tup1, tup2 );
Where
std::get<0>(result) == std::make_pair(1, 2.5);
std::get<1>(result) == std::make_pair('b', 2);
std::get<2>(result) == std::make_pair(-10, std::string("even strings?!"));
First, a quick overview of index arrays:
template<std::size_t ...S>
struct seq { };
// And now an example of how index arrays are used to print a tuple:
template <typename ...T, std::size_t ...S>
void print_helper(std::tuple<T...> tup, seq<S...> s) {
// this trick is exceptionally useful:
// ((std::cout << std::get<S>(tup) << " "), 0) executes the cout
// and returns 0.
// { 0... } expands (because the expression has an S in it),
// returning an array of length sizeof...(S) full of zeros.
// The array isn't used, but it's a great hack to do one operation
// for each std::size_t in S.
int garbage[] = { ((std::cout << std::get<S>(tup) << " "), 0)... };
std::cout << std::endl;
}
And now to use our print_helper function:
int main() {
print_helper(std::make_tuple(10, 0.66, 'h'), seq<0,1,2>() );
return 0;
}
Typing seq<0,1,2> can be a bit of a pain, though. So we can use template recursion to create a class to generate seqs, so that gens<3>::type is the same as seq<0,1,2>:
template<std::size_t N, std::size_t ...S>
struct gens : gens<N-1, N-1, S...> { };
template<std::size_t ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
int main() {
print_helper(std::make_tuple(10, 0.66, 'h'), gens<3>::type() );
return 0;
}
Since the N in gens<N>::type will always be the number of elements in the tuple, you can wrap print_helper to make it easier:
template <typename ...T>
void print(std::tuple<T...> tup) {
print_helper(tup, typename gens<sizeof...(T)>::type() );
}
int main() {
print(std::make_tuple(10, 0.66, 'h'));
return 0;
}
Note that the template arguments can be deduced automatically (typing all of that out would be a pain wouldn't it?).
Now, the tuple_zip function:
As before, start with the helper function:
template <template <typename ...> class Tup1,
template <typename ...> class Tup2,
typename ...A, typename ...B,
std::size_t ...S>
auto tuple_zip_helper(Tup1<A...> t1, Tup2<B...> t2, seq<S...> s) ->
decltype(std::make_tuple(std::make_pair(std::get<S>(t1),std::get<S>(t2))...)) {
return std::make_tuple( std::make_pair( std::get<S>(t1), std::get<S>(t2) )...);
}
The code is a little tricky, particularly the trailing return type (the return type is declared as auto and provided with -> after the parameters are defined). This lets us avoid the problem of even defining what the return type will be, by simply declaring it returns the expression used in the function body (if x and y are ints, delctype(x+y) is resolved at compile time as int).
Now wrap it in a function that provides the appropriate seq<0, 1...N> using gens<N>::type:
template <template <typename ...> class Tup1,
template <typename ...> class Tup2,
typename ...A, typename ...B>
auto tuple_zip(Tup1<A...> t1, Tup2<B...> t2) ->
decltype(tuple_zip_helper(t1, t2, typename gens<sizeof...(A)>::type() )) {
static_assert(sizeof...(A) == sizeof...(B), "The tuple sizes must be the same");
return tuple_zip_helper( t1, t2, typename gens<sizeof...(A)>::type() );
}
Now you can use it as specified in the question:
int main() {
auto tup1 = std::make_tuple(1, 'b', -10);
auto tup2 = std::make_tuple(2.5, 2, std::string("even strings?!"));
std::tuple<
std::pair<int, double>,
std::pair<char, int>,
std::pair<int, std::string> > x = tuple_zip( tup1, tup2 );
// this is also equivalent:
// auto x = tuple_zip( tup1, tup2 );
return 0;
}
And finally, if you provide a << operator for std::pair you can use the print function we defined above to print the zipped result:
template <typename A, typename B>
std::ostream & operator << (std::ostream & os, const std::pair<A, B> & pair) {
os << "pair("<< pair.first << "," << pair.second << ")";
return os;
}
int main() {
auto tup1 = std::make_tuple(1, 'b', -10);
auto tup2 = std::make_tuple(2.5, 2, std::string("even strings?!"));
auto x = tuple_zip( tup1, tup2 );
std::cout << "zipping: ";
print(tup1);
std::cout << "with : ";
print(tup2);
std::cout << "yields : ";
print(x);
return 0;
}
The output is:
zipping: 1 b 10
with : 2.5 2 even strings?!
yields : pair(1,2.5) pair(b,2) pair(10,even strings?!)
Like std::array, std::tuple is defined at compile time, and so it can be used to generate more optimizable code (more information is known at compile time compared to containers like std::vector and std::list). So even though it's sometimes a bit of work, you can sometimes use it to make fast and clever code. Happy hacking!
Edit:
As requested, allowing tuples of different sizes and padding with null pointers:
template <typename T, std::size_t N, std::size_t ...S>
auto array_to_tuple_helper(const std::array<T, N> & arr, seq<S...> s) -> decltype(std::make_tuple(arr[S]...)) {
return std::make_tuple(arr[S]...);
}
template <typename T, std::size_t N>
auto array_to_tuple(const std::array<T, N> & arr) -> decltype( array_to_tuple_helper(arr, typename gens<N>::type()) ) {
return array_to_tuple_helper(arr, typename gens<N>::type());
}
template <std::size_t N, template <typename ...> class Tup, typename ...A>
auto pad(Tup<A...> tup) -> decltype(tuple_cat(tup, array_to_tuple(std::array<std::nullptr_t, N>()) )) {
return tuple_cat(tup, array_to_tuple(std::array<std::nullptr_t, N>()) );
}
#define EXTENSION_TO_FIRST(first,second) ((first)>(second) ? (first)-(second) : 0)
template <template <typename ...> class Tup1, template <typename ...> class Tup2, typename ...A, typename ...B>
auto pad_first(Tup1<A...> t1, Tup2<B...> t2) -> decltype( pad<EXTENSION_TO_FIRST(sizeof...(B), sizeof...(A)), Tup1, A...>(t1) ) {
return pad<EXTENSION_TO_FIRST(sizeof...(B), sizeof...(A)), Tup1, A...>(t1);
}
template <template <typename ...> class Tup1, template <typename ...> class Tup2, typename ...A, typename ...B>
auto diff_size_tuple_zip(Tup1<A...> t1, Tup2<B...> t2) ->
decltype( tuple_zip( pad_first(t1, t2), pad_first(t2, t1) ) ) {
return tuple_zip( pad_first(t1, t2), pad_first(t2, t1) );
}
And BTW, you're going to need this now to use our handy print function:
std::ostream & operator << (std::ostream & os, std::nullptr_t) {
os << "null_ptr";
return os;
}
It isn't extremely difficult to do this for an arbitrary amount of tuples.
One way is to make a function that collects all elements at a specific index from N tuples into a new tuple. Then have another function which collects those tuples into a new tuple for each index in the original tuples.
All of that can be done relatively simply by expanding expressions with parameter packs, without any recursive functions.
#include <cstddef>
#include <tuple>
namespace detail {
// Describe the type of a tuple with element I from each input tuple.
// Needed to preserve the exact types from the input tuples.
template<std::size_t I, typename... Tuples>
using zip_tuple_at_index_t = std::tuple<std::tuple_element_t<I, std::decay_t<Tuples>>...>;
// Collect all elements at index I from all input tuples as a new tuple.
template<std::size_t I, typename... Tuples>
zip_tuple_at_index_t<I, Tuples...> zip_tuple_at_index(Tuples && ...tuples) {
return {std::get<I>(std::forward<Tuples>(tuples))...};
}
// Create a tuple with the result of zip_tuple_at_index for each index.
// The explicit return type prevents flattening into a single tuple
// when sizeof...(Tuples) == 1 or sizeof...(I) == 1 .
template<typename... Tuples, std::size_t... I>
std::tuple<zip_tuple_at_index_t<I, Tuples...>...> tuple_zip_impl(Tuples && ...tuples, std::index_sequence<I...>) {
return {zip_tuple_at_index<I>(std::forward<Tuples>(tuples)...)...};
}
}
// Zip a number of tuples together into a tuple of tuples.
// Take the first tuple separately so we can easily get its size.
template<typename Head, typename... Tail>
auto tuple_zip(Head && head, Tail && ...tail) {
constexpr std::size_t size = std::tuple_size_v<std::decay_t<Head>>;
static_assert(
((std::tuple_size_v<std::decay_t<Tail>> == size) && ...),
"Tuple size mismatch, can not zip."
);
return detail::tuple_zip_impl<Head, Tail...>(
std::forward<Head>(head),
std::forward<Tail>(tail)...,
std::make_index_sequence<size>()
);
}
See it in action here: https://wandbox.org/permlink/EQhvLPyRfDrtjDMw
I used some C++14/17 features, but nothing essential. The most difficult part to replace would be the fold expression for checking the tuple sizes. That would probably have to become a recursive check.