代码之家  ›  专栏  ›  技术社区  ›  Artefacto

将参数转换为包装函数所需的类型

  •  0
  • Artefacto  · 技术社区  · 4 年前

    假设我想要一个泛型函数 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);
        }
    }
    

    问题是在评论的地方应该放什么。

    (附带问题:有没有办法避免在调用时描述F的参数类型 wrap ?)

    0 回复  |  直到 4 年前
        1
  •  1
  •   Evgeny S.    4 年前

    下面是一个使用 Boost.Hana 具有 func 作为模板参数。

    #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 .

        2
  •  1
  •   Jarod42    4 年前

    不确定这是你想要的,但是 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