I'm trying to write a generic function that concatenates two functions that can be called with the same set of arguments but I'm having a bit of trouble. Here's what I have so far (it doesn't compile)
//A functor to store the input functions and call them
template <typename LEFT, typename RIGHT>
struct combine_functions {
combine_functions(const LEFT &left, const RIGHT &right)
: left(left), right(right) {}
template <typename ...ARGS>
std::enable_if_t<
//My compiler doesn't have support for C++17 std library so I
//found an implementation of callable on SO
is_callable_v<LEFT, std::decay_t<ARGS>...> &&
is_callable_v<RIGHT, std::decay_t<ARGS>...>
> operator()(ARGS... args) const {
//the return value doesn't matter in my situation and can be
//completely discarded
left(std::forward<ARGS>(args)...);
right(std::forward<ARGS>(args)...);
}
private:
mutable LEFT left;
mutable RIGHT right;
};
//I should probably have an enable if that checks the arguments
//are function pointers or functors
template <typename LEFT, typename RIGHT>
combine_functions<
std::decay_t<LEFT>,
std::decay_t<RIGHT>
>
operator+(
const LEFT &left,
const RIGHT &right
) {
return {left, right};
}
If it is unclear what I'm trying to achieve then here is a test.
#include <iostream>
#include "combine functions.hpp"
struct A {
void operator()(float &f, int i) {
std::cout << "running A with float " << f << " and int " << i << '\n';
f++;
}
};
struct B {
void operator()(float &f, int i) {
std::cout << "running B with float " << f << " and int " << i << '\n';
f++;
}
};
struct C {
void operator()(float &f, int i) {
std::cout << "running C with float " << f << " and int " << i << '\n';
f++;
}
};
int main(int, const char**) {
A a;
B b;
C c;
auto abc = concat(concat(a, b), c);
//or
//auto abc = a + b + c;
std::function<void(float &, int)> abcFunc = abc;
float f = 5.0f;
int i = 9;
abcFunc(f, i);
return EXIT_SUCCESS;
}
And here is the expected output
running A with float 5 and int 9
running B with float 6 and int 9
running C with float 7 and int 9
I think this is a reasonable starting point. Supports any number of concatenations and any number of arguments with perfect forwarding:
#include <tuple>
#include <utility>
#include <iostream>
namespace detail
{
template<class Tuple, std::size_t...Is, class...Args>
void exec(Tuple&& tuple, std::index_sequence<Is...>, Args&&...args)
{
using expand = int[];
void(expand{
0,
(std::get<Is>(tuple)(std::forward<Args>(args)...),0)...
});
}
}
template<class...Funcs>
auto concat(Funcs&&...funcs)
{
constexpr auto nof_funcs = sizeof...(funcs);
return [funcs = std::make_tuple(std::forward<Funcs>(funcs)...)](auto&&...args) mutable
{
detail::exec(funcs,
std::make_index_sequence<nof_funcs>(),
std::forward<decltype(args)>(args)...);
};
};
int main()
{
auto f1 = [](auto&& arg) { std::cout << arg << std::endl; };
auto f2 = [](auto&& arg) { std::cerr << arg << std::endl; };
concat(f1, f2)("Hello, World");
}