std::transform provides overloads which take either a unary (one argument) or binary (two argument) callable operation (typically a lambda).
I would like to pass my desired callable as an argument to a parent function, and use a compile-time (e.g. template metaprogramming) approach to automatically select which of the std::transform
overloads gets used, based on whether the passed callable has a function signature with one or two arguments.
Here is the desired approach expressed in (not yet working) code:
#include <algorithm>
auto UnaryOp = [](const auto& src) { return src; }; // simple copy
auto BinaryOp = [](const auto& src1, const auto& src2) {return src1 + src2; }; // accumulate
auto GenericTransformer = [](auto src, auto dst, auto operation) { // operation is unary OR binary
/* unrelated code */
// need to chose this one:
std::transform(src.begin(), src.end(), dst.begin(), operation);
// or this one:
std::transform(src.begin(), src.end(), dst.begin(), dst.begin(), operation);
// depending on whether unary or binary operation is passed in 'operation' argument
/* unrelated code */
};
int main() {
std::vector<int> source_vec(100);
std::vector<int> dest_vec(100);
GenericTransformer(source_vec, dest_vec, UnaryOp); // i.e. copy source to destination
GenericTransformer(source_vec, dest_vec, BinaryOp); // i.e. accumulate source into destination
}
Here I have defined two lambda operations - one unary and one binary (UnaryOp
and BinaryOp
) – which are passed to the GenericTransformer()
from main()
.
Within GenericTransformer()
, what compile-time magic can I use to automatically select which of the two std::transform()
calls gets made based on the function signature of the operation
argument?
NOTE: this is a simplified case for example purposes. I would prefer not to have to split GenericTransformer()
into two separate functions (a unary one and a binary one), as that would result in a lot of duplication of code not shown here. Sticking to the DRY philosophy!
With C++17 you can mix if constexpr
and std::is_invocable
:
if constexpr (std::is_invocable_v<
decltype(operation), decltype(*src.begin())>) {
std::transform(src.begin(), src.end(), dst.begin(), operation);
}
else {
std::transform(src.begin(), src.end(), dst.begin(), dst.begin(), operation);
}
You could also check that operation
is valid in the second case but that would require an extra else
branch to avoid silencing compile-time errors when (neither branch is valid), and this would require some always_false
shenanigan.
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