C ++将同构包装类型的元组转换为原始类型的元组

时间:2019-01-23 09:18:13

标签: c++ c++17 wrapper variadic-templates stdtuple

我想调用std::apply()到一个函数;但是,我无法使用,因为我使用的std::tuple当前已包装。例如:

#include <tuple>

template <class T>
struct wrapped
{
    wrapped(T t) : t(t) {}

    T t;
};

template <class T, class ... Args>
struct delay_call
{
    T(*callback)(Args...);
    std::tuple<Args...> params;

    delay_call(T(*callback)(Args...), Args ... params) :
        callback(callback), params(params...)
    {}

    T call()
    {
        return std::apply(callback, params);
    }
};

template <class T, class ... Args>
struct delay_call_with_wrap
{
    T(*callback)(Args...);
    std::tuple<wrapped<Args>...> w_params;

    delay_call_with_wrap(T(*callback)(Args...), wrapped<Args> ... w_params) :
        callback(callback), w_params(w_params...)
    {}

    T call()
    {
        std::tuple<Args...> params; // = w_params.t
        return std::apply(callback, actual_params);
    }
};

float saxpy(float a, float x, float y)
{
    return a * x + y;
}

int main()
{
    float a = 1, x = 2, y = 3;
    delay_call delay_saxpy(saxpy, a, x, y);

    wrapped w_a = 1.f, w_x = 2.f, w_y = 3.f;
    delay_call_with_wrap w_delay_saxpy(saxpy, w_a, w_x, w_y);

    float result = delay_saxpy.call();

    float w_result = w_delay_saxpy.call();
}

delay_call结构正常工作;但是,我不确定如何提取每个元组元素的实际值并将其提供给std::apply()来执行。

简而言之,对于delay_call_with_wrap::call,我如何将std::tuple<wrapped<Args>...>转换为std::tuple<Args...>

3 个答案:

答案 0 :(得分:4)

我会完全避免使用std::apply并通过使用callback解开元组直接调用std::index_sequence

template <std::size_t ...I> T call_low(std::index_sequence<I...>)
{
    return callback(std::get<I>(w_params).t...);
}

T call()
{
    return call_low(std::make_index_sequence<sizeof...(Args)>{});
}

答案 1 :(得分:2)

  

简而言之,对于delay_call_with_wrap::call,我如何将std::tuple<wrapped<Args>...>转换为std::tuple<Args...>

在我看来,最好避免使用旧的std::apply() / std::make_index_sequence方法std::index_sequence(请参见HolyBlackCat答案)。

但是,如果您真的想使用std::apply(),则可以第一次调用它来拆开元组(以获取未包装值的元组),然后照常调用。

我的意思是

T call ()
 {
   auto actual_params = std::apply([](auto ... wt){ return std::make_tuple(wt.t...); },
      w_params);
   return std::apply(callback, actual_params);
}

或者直接在一个呼叫中

T call()
 {
   return std::apply(callback,
             std::apply([](auto ... wt){ return std::make_tuple(wt.t...); },
                w_params));
 }

恕我直言,如果w_param成员是常数,那么此解决方案是合理的,因此您可以一次计算所有actual_params并将其设为static

答案 2 :(得分:1)

在实践中可能不是最好的解决方案,但这是使用可变模板化lambda以避免index_sequence的解决方案:

template <class T, class ... Args>
struct delay_call_with_wrap
{
    T(*callback)(Args...);
    std::tuple<wrapped<Args>...> w_params;

    delay_call_with_wrap(T(*callback)(Args...), wrapped<Args> ... w_params) :
        callback(callback), w_params(w_params...)
    {}

    T call()
    {
        auto helper = [this] <class ... Args_> (wrapped<Args_>... args)
        {
            return callback(args.t...);
        };
        return std::apply(helper, w_params);
    }
};

Demo

想法是只提供一个与std::apply在此处产生的参数匹配的函数-它需要使用wrapped<Args>...。从那里开始,在提取打包值的同时扩展包很简单。

我们使用lambda是因为std::apply需要一个Callable,因此我们不能仅使用另一个成员函数。好吧,我想我们可以overload operator()换成delay_call_with_wrap。那样会使人感到困惑,但至少不限于C ++ 2a(和missing compiler support),如模板化的lambda。