Reputation: 3
I currently have a number of data accessors like:
template <typename Fn>
typename std::result_of<Fn(DataA)>::type GetDataA(Fn fn) {
DataA a = DoSomeWorkToGetA();
return fn(a);
}
template <typename Fn>
typename std::result_of<Fn(DataB)>::type GetDataB(Fn fn) {
DataB b = DoSomeWorkToGetB();
return fn(b);
}
Which are used like:
auto computation_result_a = GetDataA([](DataA a) {
return DoCustomProcessingOnA(a);
});
auto computation_result_b = GetDataB([](DataB a) {
return DoCustomProcessingOnB(b);
});
What I'd like, is for a way to automatically generate combinations of these Getters, like:
template <typename Fn>
typename std::result_of<Fn(DataA, DataB)>::type GetDataAandB(Fn fn) {
DataA a = GetDataA([](DataA a) { return a; });
DataB b = GetDataB([](DataB b) { return b; });
return fn(a, b);
}
I have seen this type of thing done in the past, with a user API like:
auto computation_result_a = GetData<A>([](Data a) { /**/ });
auto computation_result_b = GetData<A>([](Data b) { /**/ });
auto computation_result_ab = GetData<A,B>([](Data a, Data b) { /**/ });
But am unsure how to accomplish this.
Upvotes: 0
Views: 88
Reputation: 22162
First, you will need a map from type (or tag) to correct DoSomeWorkToGet*
function, e.g.:
template<typename T> /* or an enum instead of typename */
void DoSomeWorkToGet() = delete;
template<>
auto DoSomeWorkToGet<A>() { return DoSomeWorkToGetA(); }
template<>
auto DoSomeWorkToGet<B>() { return DoSomeWorkToGetB(); }
Then writing the function is just a matter of using a variadic template and a pack expansion:
template<typename... Ts, typename F> /* or an enum instead of the first typename */
auto GetData(F f) {
return f(DoSomeWorkToGet<Ts>()...);
}
To be used as e.g.:
auto computation_result_ab = GetData<A, B>([](auto a, auto b) {
return /* something with a and b */;
});
If you require the DoSomeWorkToGet
functions to be evaluated in-order left-to-right, you cannot use the function above, but you can use the following:
template<typename... Ts, typename F> /* or an enum instead of the first typename */
auto GetData(F f) {
using tuple_type = std::tuple<decltype(DoSomeWorkToGet<Ts>())&&...>;
return std::apply(f, tuple_type{DoSomeWorkToGet<Ts>()...});
}
This requires C++17. Implementation with C++11/C++14 is also possible, but a will need slightly more work (to implement std::apply
or a weaker form of it). It guarantees the evaluation order, because list-initialization (used in tuple_type{...}
) is always strictly sequenced left-to-right.
Upvotes: 1