Вот интересный вариант вашей идеи. Передайте кортеж функций и пакет аргументов. Затем вызовите все функции из этого кортежа (слева направо), которые могут принимать эти заданные аргументы. Кроме того, сохраните все возвращаемые значения (если они есть) из тех функций, которые вызывают в кортеже. Здесь я использую void_t
(std::void_t
в C++ 17) аспект SFINAE:
#include <iostream>
#include <tuple>
#include <type_traits>
template <typename T>
using void_t = void;
template <std::size_t N, typename VoidTFailed, typename EnableIfFailed, typename Tuple, typename... Args>
struct Try {
static bool execute (Tuple&&, Args&&...) {
std::cout << "No function found.\n";
return false;
}
};
template <std::size_t N, typename VoidTFailed, typename Tuple, typename... Args>
struct Try<N, VoidTFailed, std::enable_if_t<(N < std::tuple_size<Tuple>::value)>, Tuple, Args...> : std::conditional_t<(N < std::tuple_size<Tuple>::value - 1),
Try<N+1, void, void, Tuple, Args...>,
Try<N+1, int, int, Tuple, Args...> // If N == std::tuple_size<Tuple>::value - 1, then simply inherit from the primary template above, as inheriting from Try<N+1, void, void, Tuple, Args...> will cause a compiling error when std::tuple_element_t<N+1, Tuple> is instantiated in the specialization below.
> {};
struct NoReturnValue {
friend std::ostream& operator<< (std::ostream& os, const NoReturnValue&) {
return os << "[no value returned]";
}
};
template <std::size_t N, typename Tuple, typename EnableIfFailed, typename... Args>
struct Continue {
static auto execute (Tuple&& functionTuple, Args&&... args) {
const auto singleTuple = std::make_tuple (std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...));
return std::tuple_cat (singleTuple, Try<N+1, void, void, Tuple, Args...>::execute(std::forward<Tuple>(functionTuple), std::forward<Args>(args)...));
}
};
template <std::size_t N, typename Tuple, typename... Args> // This specialization is only used if the function in question returns void.
struct Continue<N, Tuple, std::enable_if_t<std::is_void<decltype(std::declval<std::tuple_element_t<N, std::decay_t<Tuple>>>()(std::declval<std::decay_t<Args>>()...))>::value>, Args...> {
static auto execute (Tuple&& functionTuple, Args&&... args) {
std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...);
return std::tuple_cat (std::make_tuple(NoReturnValue{}), Try<N+1, void, void, Tuple, Args...>::execute(std::forward<Tuple>(functionTuple), std::forward<Args>(args)...));
}
};
template <std::size_t N, typename Tuple, typename EnableIfFailed, typename... Args>
struct Stop {
static auto execute (Tuple&& functionTuple, Args&&... args) {
return std::make_tuple (std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...));
}
};
template <std::size_t N, typename Tuple, typename... Args> // This specialization is only used if the function in question returns void.
struct Stop<N, Tuple, std::enable_if_t<std::is_void<decltype(std::declval<std::tuple_element_t<N, std::decay_t<Tuple>>>()(std::declval<std::decay_t<Args>>()...))>::value>, Args...> {
static auto execute (Tuple&& functionTuple, Args&&... args) {
std::get<N>(std::forward<Tuple>(functionTuple))(std::forward<Args>(args)...);
return std::make_tuple(NoReturnValue{});
}
};
template <std::size_t N, typename Tuple, typename... Args>
struct Try<N, void_t<decltype(std::declval<std::tuple_element_t<N, Tuple>>()(std::declval<Args>()...))>,
std::enable_if_t<(N < std::tuple_size<Tuple>::value)>, Tuple, Args...> : std::conditional_t<(N < std::tuple_size<Tuple>::value - 1),
Continue<N, Tuple, void, Args...>,
Stop<N, Tuple, void, Args...>
> {};
template <typename Tuple, typename... Args>
auto executeAllPossible (Tuple&& functionTuple, Args&&... args) {
return Try<0, void, void, Tuple, Args...>::execute(std::forward<Tuple>(functionTuple), std::forward<Args>(args)...);
}
// Testing
int foo (int, char, bool, int, int) {std::cout << "foo\n"; return 8;}
bool bar (int, char, bool, double, long, bool) {std::cout << "bar\n"; return true;}
void goo (int, char&&, bool, float, int) {std::cout << "goo\n";}
double baz (int, const char, bool&&, double, std::size_t) {std::cout << "baz\n"; return 4.5;}
template <typename Ch, typename Tr, typename Tuple, std::size_t... Is>
void print_tuple_impl (std::basic_ostream<Ch, Tr>& os, const Tuple& t, std::index_sequence<Is...>) {
using A = int[];
(void)A{(void(os << (Is == 0 ? "" : ", ") << std::get<Is>(t)), 0)...};
}
template <typename Ch, typename Tr, typename... Args>
decltype(auto) operator<< (std::basic_ostream<Ch, Tr>& os, const std::tuple<Args...>& t) {
os << "tuple{";
print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
return os << "}";
}
int main() {
const auto tuple = executeAllPossible (std::make_tuple(foo, bar, goo, baz), 2, 'c', true, 3.5, 8);
std::cout << std::boolalpha << "output = " << tuple << '\n';
}
Выход:
foo goo baz
output = tuple{8, [no value returned], 4.5}
Марка 'a' и' b' тем же именем. – songyuanyao
Работает для конкретного примера, но не для шаблонов, подобных моему for_each. Вы получите переопределение шаблона ... for_each. (Я отредактировал пример, чтобы лучше понять мою проблему.) – csiz
Вы слышали о SFINAE? Отправка тегов? Классы признаков? – Yakk