在元组中搜索函数的参数 [英] Searching through a tuple for arguments of a function

查看:62
本文介绍了在元组中搜索函数的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑此问题

int foo (int a, char c, bool b) {std::cout << a << ' ' << c << ' ' << b << '\n';  return 8;}
double bar (int a, char c, bool b, int d) {std::cout << a << ' ' << c << ' ' << b << ' ' << d << '\n';  return 2.5;}
char baz (bool a, bool b) {std::cout << a << ' ' << b << '\n';  return 'a';}

int main() {
    const auto tuple = std::make_tuple(5, true, 'a', 3.5, false, 1000, 't', 2, true, 5.8);
    const std::tuple<int, double, char> t = searchArguments (tuple, foo, bar, baz);
}

因此 foo (来自 tuple )。从左到右搜索,找到的第一个整数是 5 ,找到的第一个字符是 a ,第一个布尔值找到的是 true 。因此,将调用 foo(5,a,true)。对于 bar baz 同样。除了 bar 需要2个整数,我们不希望它两次接受 5 ,而希望 5 ,然后 1000 。同样, baz 将以(true,false)作为参数,而不是(true ,真的)

So the arguments for foo are first searched (from tuple). Searching from left to right, the first int found is 5, the first char found is a, and the first bool found is true. So then foo(5,a,true) is called. Similarly for bar and baz. Except bar takes 2 ints, and we don't want it to take 5 twice, but rather 5 and then 1000. Similarly, baz is to take (true, false) for its arguments instead of (true, true).

不幸的是,我当前的解决方案输出的正是我刚才所说的内容,而不应该输出:

My current solution below unfortunately outputs precisely what I just said should not be outputted:

foo(5,a,true)  // OK
bar(5,a,true,5)  // Nope, we want bar(5,a,true,1000)
baz(true,true)  // Nope, we want baz(true,false)

我意识到一种解决当前解决方案的方法(很丑):

I realize that one possible (ugly) way to fix my current solution:

#include <iostream>
#include <tuple>
#include <utility>

// C++17 std::apply
template <typename F, typename Tuple, size_t... Is>
auto apply_impl (F&& f, Tuple&& tuple, const std::index_sequence<Is...>&) {
    return (std::forward<F>(f))(std::get<Is>(std::forward<Tuple>(tuple))...);
}

template <typename F, typename Tuple>
auto apply (F&& f, Tuple&& tuple) {  // Invoke the Callable object f with a tuple of arguments. 
    return apply_impl(std::forward<F>(f), std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}

// FunctionTraits
template <typename> struct FunctionTraits;

template <typename R, typename... Args>
struct FunctionTraits<R(Args...)> : std::integral_constant<std::size_t, sizeof...(Args)> {
    using args_type = std::tuple<Args...>;
    using return_type = R;
};

template <typename R, typename... Args>
struct FunctionTraits<R(*)(Args...)> : FunctionTraits<R(Args...)> {};

template <typename R, typename... Args>
struct FunctionTraits<R(&)(Args...)> : FunctionTraits<R(Args...)> {};
// etc... for other callable types.

namespace getFirstDetail {
    template <typename T, typename Tuple, std::size_t N, bool>
    struct SearchTuple : SearchTuple<T, Tuple, N+1, std::is_same<std::tuple_element_t<N+1, Tuple>, T>::value> {};

    template <typename T, typename Tuple, std::size_t N>
    struct SearchTuple<T, Tuple, N, true> {
        static T search (const Tuple& tuple) {return std::get<N>(tuple);}
    };
}

// Get the first element of a tuple whose type is T.  Note that using std::get<T> will not suffice since this fails to compile if the tuple has more than one element of type T.
// It is the client's responsiblity to ensure that such an element in the tuple exists (else there will be a crash).
template <typename T, typename Tuple>
T getFirst (const Tuple& tuple) {
    return getFirstDetail::SearchTuple<T, Tuple, -1, false>::search(tuple);
}

namespace searchArgumentsDetail {   
    template <typename> struct Search;

    template <typename... Args>
    struct Search<std::tuple<Args...>> {
        template <typename R, typename Tuple, typename F>
        static R execute (const Tuple& tuple, F f) {return apply(f, std::make_tuple(getFirst<Args>(tuple)...));}
    };
}

template <typename Tuple>
std::tuple<> searchArguments (const Tuple&) {return std::tuple<>();}

// Gathers the first possible elements from 'tuple' that 'f' can accept (reading from left to right) and carries out the function.  Then it is repeated for the remaining functions fs...
template <typename Tuple, typename F, typename... Fs>
auto searchArguments (const Tuple& tuple, F f, Fs... fs) {
    using ArgsType = typename FunctionTraits<F>::args_type;
    using ReturnType = typename FunctionTraits<F>::return_type;
    const auto singleTuple = std::make_tuple (searchArgumentsDetail::Search<ArgsType>::template execute<ReturnType>(tuple, f));
    return std::tuple_cat (singleTuple, searchArguments (tuple, fs...));
}

// Testing
int foo (int a, char c, bool b) {std::cout << a << ' ' << c << ' ' << b << '\n';  return 8;}
double bar (int a, char c, bool b, int d) {std::cout << a << ' ' << c << ' ' << b << ' ' << d << '\n';  return 2.5;}
char baz (bool a, bool b) {std::cout << a << ' ' << b << '\n';  return 'a';}

int main() {
    const auto tuple = std::make_tuple(5, true, 'a', 3.5, false, 1000, 't', 2, true, 5.8);
    std::cout << std::boolalpha;
    const std::tuple<int, double, char> t = searchArguments (tuple, foo, bar, baz);
    std::cout << std::get<0>(t) << ' ' << std::get<1>(t) << ' ' << std::get<2>(t) << '\n';  // 8 2.5 a
    std::cin.get();
}

是从元组中删除每个使用的元素,并将较小的元组传递给下一次递归,从而确保这些重复参数不会发生。但这真是一团糟(可能效率低下)。此外,在调用下一个函数时,我们需要再次使用原始元组重新启动,因此原始元组以及每个截短的元组都必须传递。我只是想问一想,在我跳入这个噩梦般的任务之前,是否有比这个更好,更优雅的解决方案(如果它甚至可以工作的话)。

is to remove each element used from the tuple and pass the smaller tuple to the next recursion, thus guaranteeing that those repeat arguments don't occur. But that's a real mess (and probably unnecessarily inefficient). Furthermore, when calling up the next function, we need to restart with the original tuple again, and thus the original tuple must be passed around as well as each truncated tuple. I just want to ask if there is a better, much more elegant solution than this before I leap into this nightmarish task (if it even works at all).

更新:我想到的一个新想法(如果只是试图解决当前解决方案)是将我的 getFirst 函数修改为 getN< N ...> ; ,其中N = 1表示获得第一个,N = 2意味着获得第二个,等等...?但是,有责任更新最新的N值。

Update: A new idea I thought of (if simply trying to fix my current solution), is to modify my getFirst function to getN<N...>, where N = 1 means get the first, N = 2 means get the second, etc...? But then there is the responsibility of updating the latest N value.

推荐答案

#include <utility>
#include <type_traits>
#include <tuple>

namespace detail {

template <std::size_t, int, typename, typename, typename=void>
constexpr std::size_t find = -1;
template <std::size_t I, int dir, typename U, typename Ts>
constexpr auto find<I, dir, U, Ts, std::enable_if_t<(I < std::tuple_size<Ts>{})>>
 = std::is_same<std::tuple_element_t<I, Ts>, U>{}? I : find<I+dir, dir, U, Ts>;

template <typename, typename ISeq, std::size_t, typename>
struct obtain_indices {using type = ISeq;};
template <typename Ts, std::size_t... Is, std::size_t u, typename Us>
struct obtain_indices<Ts, std::integer_sequence<
  std::enable_if_t<(u < std::tuple_size<Us>{}), std::size_t>, Is...>, u, Us> {
    static constexpr std::array<std::size_t, sizeof...(Is)> indices = {Is...};
    using C = std::tuple_element_t<u, Us>;
    static constexpr auto previous = find<u-1, -1, C, Us>;
    using type = typename obtain_indices<Ts, std::index_sequence<Is...,
      find<previous != -1? indices[previous]+1 : 0, 1, C, Ts>>, u+1, Us>::type;
};

// General overload once indices have been determined
template <typename Tup, typename F, std::size_t... Is>
constexpr decltype(auto) invoke(F&& f, Tup&& t,
  std::index_sequence<Is...>) {
    return std::forward<F>(f)(std::get<Is>(std::forward<Tup>(t))...);
}

} // end namespace detail

// For function pointers
template <typename Tup, typename R, typename... Args>
constexpr decltype(auto) invoke(R(*f)(Args...), Tup&& t) {
    return detail::invoke(f, std::forward<Tup>(t),
      typename detail::obtain_indices<std::decay_t<Tup>,
        std::index_sequence<>, 0, std::tuple<std::decay_t<Args>...>>::type{});
}

根据您的示例:

#include <iostream>

double bar (int a, char c, bool b, int d) {
    std::cout << a << ' ' << c << ' ' << b << ' ' << d << '\n';
    return 2.5;
}

int main() {
    const auto tuple = std::make_tuple(5, true, 'a', 3.5,
                                       false, 1000, 't', 2, true, 5.8);
    invoke(bar, tuple);
}

演示

这篇关于在元组中搜索函数的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆