Polymorphic (Generic) Functions as Arguments in C++
c++, generics, higher-kinded-types, higher-rank-types, variadic-templates
Solution
I think you can do this pretty simply without C++14 at all. I'm going to assume a few things about how your `Tuple` is constructed, namely that these two ctors exist:
Tuple(First, Rest... ); // (1)
Tuple(First, const Tuple<Rest...>& ); // (2)
We need one type trait: given a function that we're transforming with, we need to know what types it produces:
template <typename T, typename F>
using apply_t = decltype(std::declval<F>()(std::declval<T>()));
(God I love C++11)
With that, we can determine the return type easily, and it's just a matter of calling the function recursively:
template <typename First, typename... Rest>
struct Tuple
{
template <typename F>
Tuple<apply_t<First, F>, apply_t<Rest, F>...>
Transform(F func)
{
return {func(first), rest.Transform(func)}; // hence the need
// for ctor (2)
};
};
(Depending on how you wrote your `Tuple` you may or may not need a base case for a trivial transform that just returns a `Tuple<>`, or a base case that just returns a `Tuple<apply_t<First, F>>`. Either way, not a big deal).
And you don't even need to specialize `Tuple` at all. You just need to pass in the right functors. For instance:
struct Zero
{
template <typename T>
int operator()(T ) { return 0; }
};
struct Incr
{
template <typename T>
T operator()(T x) { return x + 1; }
};
Tuple<int, double, char> tup(1, 2.0, 'c');
auto z = tup.Transform(Zero{}); // z is Tuple<int, int, int>{0, 0, 0}
auto i = tup.Transform(Incr{}); // i is Tuple<int, double, char>{2, 3.0, 'd'}
Here's a full code example, logging all the types too. Of course, with C++14, we can do those inline:
auto i2 = tup.Transfom([](auto x) -> decltype(x) {return x+1; });
// i2 is a Tuple<int, double, char>{2, 3.0, 'd'};
// without the trailing decltype, it gets deduced as Tuple<int, double, int>.
Problem
I am developing a relatively simple program (a calculator actually). However, I have decided to make all components of my program as generic as possible because: - It's good practice. - It keeps things interesting. As part of this program I am using a `Tuple` class that I am writing. I know that a class already exists, but I like having complete control over my code and this is only an exercise. One thing I need to do is transform a tuple of expressions (where expressions themselves are generic) into a tuple containing the result of the expressions' evaluations. In short, I have (with trivial parts left out): ``` template <class T> class Expression { public: virtual T Eval() = 0; // ... }; template <class First, class ... Rest> class Tuple { // ... private: First first; Tuple<Rest ...> rest; }; ``` And I would like to specialize over a tuple of a generic type like this: ``` template <template <class> class R, class First, class ... Rest> class Tuple<R<First>, R<Rest> ...> { // and here is the problem: Tuple<First, Rest ...> Transform(function<template<class T> T(R<T>)>); }; ``` After which I could do this: ``` template <class T> // There has to be a better way to do this T Eval(Expression<T>& expr){ return expr.Eval(); } // ... Tuple<First, Rest ...> tuple = exprs.Transform(Eval); ``` There are a few places here where I am not sure how to go about things and a real expert who could help me out here would be appreciated. I expect this code will not compile because of minor flaws but that isn't the point - My primary worry is the line I marked. If I recall correctly from the brief period I learned Haskell this function should be of Rank-2 (If not please comment and I will remove the tag). It just doesn't look right. Is there any way to do this? Update: I was advised to try pass a functor with a generic `operator ()` as a template argument but that didn't work either.