【问题标题】:Convert arguments to types expected by wrapped function将参数转换为包装函数预期的类型
【发布时间】:2020-10-31 20:04:28
【问题描述】:

假设我想要一个通用函数F 接受Args 类型的参数并返回R。我想包装这个函数,使其适合表单:

using gen_type = void (*)(struct value *values,
                          size_t num_args,
                          struct value *return_value)

我还有与通用 struct value 相互转换的函数:

template<T> T from_value(struct value);
// or have something like from_value_(struct value, int&)
// called from from_value
template<> from_value<int>(struct value);
struct value to_value(int i);

现在,我可以有这样的功能吗:

template<typename F, typename ... Args>
gen_type wrap(F func) {
    return [](struct value *values,
              size_t num_args,
              struct value *return_value) {
        if (num_args != sizeof...(Args)) { /* error */ }
        auto res = func(/* apply from_value<> to each argument type */)
        *return_value = to_value(res);
    }
}

问题是评论在哪里应该怎么写。

(附带问题:有什么方法可以避免在调用wrap 时必须描述 F 的参数类型?)

【问题讨论】:

  • 你可以使用外部库吗?如果是,那么有几种方法可以在Boost.Hana 的帮助下完成。
  • 为什么要将 C 风格的结构与现代 C++ 混合?
  • 不会飞。您的 lambda 需要捕获 func,但带有捕获的 lambda 不能转换为普通函数指针。 gen_type 不够大,无法存储任意可调用对象。
  • 如何拨打func;这可以通过辅助函数和std::make_index_sequence 轻松完成
  • Re: 避免描述F 的类型。一般不会,因为一个可调用对象可能有多个operator() 的重载,或者operator() 可能是一个函数模板。是的,如果您将自己限制在纯函数指针和/或只有一个非模板 operator() 的类。

标签: c++ c++14 variadic-templates


【解决方案1】:

以下是如何使用Boost.Hanafunc 作为模板参数的示例。

#include <iostream>
#include <math.h>
#include <boost/hana.hpp>

namespace hana=boost::hana;

// sample value type
struct value
{
    size_t content;
};

// sample conversion functions for sample value type

template <typename T>
T from_value(const value& val)
{
    return T(val.content);
}
template <>
int from_value<int>(const value& val)
{
    return val.content*2;
}
template <>
float from_value<float>(const value& val)
{
    return val.content*3.0;
}

template <typename T>
value to_value(const T& val)
{
    return value{static_cast<size_t>(round(val))};
}

// concatenate results of from_value to tuple

template <typename ...>
struct concat_values
{
};

template <typename T>
struct concat_values<T>
{
    template <typename ArrT>
    static auto apply(size_t index,const ArrT& arr)
    {
        return hana::make_tuple(from_value<T>(arr[index]));
    }
};

template <typename T, typename ... Types>
struct concat_values<T,Types...>
{
    template <typename ArrT>
    static auto apply(size_t index,const ArrT& arr)
    {
        return hana::prepend(concat_values<Types...>::apply(index+1,arr),
                            from_value<T>(arr[index])
                            );
    }
};

// wrap lambda
template <typename FuncT, FuncT func, typename ... Args>
auto wrap()
{
    return [](value *values,
            size_t num_args,
            value *return_value)
    {
        if (num_args != sizeof...(Args)) { throw std::runtime_error("Invalid number of arguments!"); }

        auto res=hana::unpack(
            concat_values<Args...>::apply(0,values),
            *func
        );
        *return_value = to_value(res);
    };
}

// try it

// sample func
double sample_sum(size_t a, int b, float c)
{
    return a+b*2+c*3;
}

// sample function with C-style signature that accepts wrapped function
void sample_invoke(void (*f)(value*,size_t,value*))
{
    value inputs[3]={{1},{2},{3}};
    value result{0};
    (*f)(inputs,3,&result);

    std::cout<<"Result "<<result.content<<std::endl;
}

// run
int main()
{
    auto wrapped=wrap<decltype(&sample_sum),&sample_sum,size_t,int,float>();
    sample_invoke(wrapped);
    return 0;
}

打印:

Result 36

Demo

更新

std::index_sequence 的另一个实现:

// apply function
template <typename ... Args, typename FuncT, std::size_t... Idx>
auto apply_func(FuncT func,value* values,std::index_sequence<Idx...>)
{
    return func(from_value<Args>(values[Idx])...);
}    

// wrap lambda
template <typename FuncT, FuncT func, typename ... Args>
auto wrap()
{
    return [](value *values,
            size_t num_args,
            value *return_value)
    {
        if (num_args != sizeof...(Args)) { throw std::runtime_error("Invalid number of arguments!"); }

        auto res=apply_func<Args...>(*func,values,std::index_sequence_for<Args...>());
        *return_value = to_value(res);
    };
}

直播Demo.

【讨论】:

  • 感谢这让我走上了正轨,尽管我最终使用了 std::tuple 而不是 hana::tuplestd::index_sequence,就像 Jarod42 建议的那样(而不是递归)。
【解决方案2】:

不确定是不是您想要的,但std::any (C++17) 似乎符合您的要求:

template <typename Sig> struct callable_trait;

// Miss all combinations of cv qualifier, ref qualifier, C-ellipsis
template <typename R, typename C, typename ... Args>
struct callable_trait<R (C::*)(Args...) const>
{
    using Ret = R;
    using ArgsTuple = std::tuple<Args...>;
};

// assume no overloads, and const non template operator from above minimal specialization
template <typename C> struct callable_trait : callable_trait<decltype(&C::operator())> {};


template <typename F>
auto wrap(F func) {
    return [=](const std::any values[], std::size_t num_args, /* std::span<std::any> */
              std::any* return_value) {
        using ArgsTuple = typename callable_trait<F>::ArgsTuple;
        constexpr std::size_t size = std::tuple_size_v<ArgsTuple>;
        if (num_args != size) { throw std::runtime_error("wrong number of arguments");/* error */ }
        // C++20 construct actually, you might create free function instead prior that
        *return_value = [=]<std::size_t...Is>(std::index_sequence<Is...>){
            return func(std::any_cast<std::tuple_element_t<Is, ArgsTuple>>(values[Is])...);
        }(std::make_index_sequence<size>()); // immediate call
    };
}

Demo

【讨论】:

  • 谢谢,但我理解正确,正如@IgorTandetnik 提到的,返回值不能转换为函数指针(这是需要的,因为最终目标是包装 C++ 函数以将它们公开给 C 库)。 func 可以在编译时知道,所以我猜这在原则上是可能的,但我猜函数必须作为模板参数给出,而不是作为参数。
  • 确实,func 被捕获,无法转换为函数指针,通过模板传递解决问题:Demo。与 C 一样,std::any 不是要走的路,所以也需要其他更改。
猜你喜欢
  • 2021-05-14
  • 2016-03-21
  • 2016-10-12
  • 2021-04-29
  • 2016-07-27
  • 2016-07-02
  • 1970-01-01
  • 2020-06-14
  • 2017-02-13
相关资源
最近更新 更多