I have multiple std::function
. Each of them have different input and output, and the input of one std::function
might be the output of another std::function
, which means that "serial" convert from one to another.
Maybe I can't describe it clear enough. Let code talks
std::function<bool(double)> combine(std::function<int(double)> convert1
, std::function<char(int)> convert2
, std::function<bool(char)> convert3)
{
return std::bind(convert1, convert2, convert3)//error. A function directly convert [double] to [bool] using convert1, convert2, convert3
}
Here is very simple code which I already remove the pointless code and show the core of my meaning.
So you can see convert1
do conversion from double
to int
, convert2
do conversion from int
to char
and convert3
do conversion from char
to bool
. Now I need to combine them together and so that I can directly convert double
to bool
.
And you know, I am not really want to convert double
to bool
. It's only for test.
One option to implement this is that write a help function:
bool helper(double d
, std::function<int(double)> convert1
, std::function<char(int)> convert2
, std::function<bool(char)> convert3)
{
return convert3(convert2(convert1(d)));
}
std::function<double(bool)> combine(std::function<int(double)> convert1
, std::function<char(int)> convert2
, std::function<bool(char)> convert3)
{
return helper;
}
But it's ugly code and maybe I use this conversion in a common way, which means that I should write this helper
for all kind of my conversion.
So, is there a directly way to combine these function together?
Creating a simple type traits to extract the input type of the last function
template <typename, typename...>
struct lastFnType;
template <typename F0, typename F1, typename ... Fn>
struct lastFnType<F0, F1, Fn...>
{ using type = typename lastFnType<F1, Fn...>::type; };
template <typename T1, typename T2>
struct lastFnType<std::function<T2(T1)>>
{ using type = T1; };
you can transform the apple apple's solution (+1) in a more general variadic template recursive solution
template <typename T1, typename T2>
std::function<T1(T2)> combine (std::function<T1(T2)> conv)
{ return conv; }
template <typename T1, typename T2, typename T3, typename ... Fn>
std::function<T1(typename lastFnType<std::function<T2(T3)>, Fn...>::type)>
combine (std::function<T1(T2)> conv1, std::function<T2(T3)> conv2,
Fn ... fn)
{
using In = typename lastFnType<std::function<T2(T3)>, Fn...>::type;
return [=](In const & in){ return conv1(combine(conv2, fn...)(in)); };
}
But observe that the order of the converter is inverted (call with last used converter first; so combine(convert3, convert2, convert1)
)
The following is a full example
#include <functional>
template <typename, typename...>
struct lastFnType;
template <typename F0, typename F1, typename ... Fn>
struct lastFnType<F0, F1, Fn...>
{ using type = typename lastFnType<F1, Fn...>::type; };
template <typename T1, typename T2>
struct lastFnType<std::function<T2(T1)>>
{ using type = T1; };
template <typename T1, typename T2>
std::function<T1(T2)> combine (std::function<T1(T2)> conv)
{ return conv; }
template <typename T1, typename T2, typename T3, typename ... Fn>
std::function<T1(typename lastFnType<std::function<T2(T3)>, Fn...>::type)>
combine (std::function<T1(T2)> conv1, std::function<T2(T3)> conv2,
Fn ... fn)
{
using In = typename lastFnType<std::function<T2(T3)>, Fn...>::type;
return [=](In const & in){ return conv1(combine(conv2, fn...)(in)); };
}
int fn1 (double d)
{ return d*2.0; }
char fn2 (int i)
{ return i+3; }
bool fn3 (char c)
{ return c == 'a'; }
int main ()
{
std::function<int(double)> f1 { fn1 };
std::function<char(int)> f2 { fn2 };
std::function<bool(char)> f3 { fn3 };
auto cmb = combine(f3, f2, f1);
bool b { cmb(3.2) };
}