let's suppose that there are N functions that take different inputs and that return different values:
int h(int);
string g(int);
double f(string);
Of course it is possible to write something like:
f(g(h(4)))
Suppose now that I want to store these functions f,g,h into a kind of container like:
Container c;
c.add(h);
c.add(g);
c.add(f);
Given an input for the first function (an int in the case of f) I would like these function being executed in a sequential way, with each of them taking as input the output of the previous one.
Do you think is it possible in C++? possibly without using dynamic_cast and using C++11
If Container
needs to be modifiable at run-time, you can achieve this with std::any
(or boost::any
):
struct Container
{
std::vector<std::function<std::any(std::any)>> _fns;
template <typename R, typename A>
void add(R(*f)(A))
{
_fns.emplace_back([f](std::any x) -> std::any
{
return {f(std::any_cast<A>(x))};
});
}
template <typename T>
std::any call(T x)
{
// TODO: replace with recursive version
return _fns[2](_fns[1](_fns[0](x)));
}
};
Usage:
int h(int x) { return x; }
std::string g(int x) { return std::to_string(x); }
double f(std::string x) { return x.size(); }
int main()
{
Container c;
c.add(h);
c.add(g);
c.add(f);
std::cout << std::any_cast<double>(c.call(5)) << '\n';
}
Output:
1
live example on wandbox.org
Possible recursive implementation:
template <typename T>
std::any call_impl(T x, const std::size_t next)
{
return next == _fns.size() - 1
? _fns[next](x)
: call_impl(_fns[next](x), next + 1);
}
template <typename T>
std::any call(T x)
{
return call_impl(x, 0);
}
This is more of a side note but I made something very similar for fun during the holiday season. It's too much code to fit here but you can check the code here or a live version here.
basically it's a function which returns a lambda capturing all the functions to invoke. This allows you to write the following code:
#include <iostream>
#include <string>
#include "chain.hpp"
int f(int x) { return x; }
std::string g(int x) { return std::to_string(x); }
double h(std::string x) { return x.size(); }
int main() {
auto function = fn::chain(f, g, h);
std::cout << function(5) << '\n'; // prints 1
return 0;
}
It will also try expanding retured tuples with std::apply if std::invoke does not work. This makes the following code valid:
#include <iostream>
#include <tuple>
#include "chain.hpp"
int main() {
auto test = fn::chain([](int i, int j) { return i + j; },
[](int i) { return i * 2; },
[](int i) -> std::tuple<int, int> {
return {i, i};
},
[](int i, int j) { return i + j; });
std::cout << test(1, 2) << '\n'; // prints 12
}
As I said, this was just a fun experement but maybe it will be helpful!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With