Any Solution to Unpack a Vector to Function Arguments in C++?

前端 未结 2 1755
感动是毒
感动是毒 2020-11-30 11:39

I am actually thinking of something similar to the \'*\' operator in python like this:

args = [1,2,4]
f(*args)

Is there a similar solution

相关标签:
2条回答
  • 2020-11-30 12:22

    Update to @Fernandes's answer.

    Yes, there does be a way to remove the need to specifying num_args in template parameter. This is because num_args is determined by the function signature, not the vector. What should be checked at run-time is the size of the vector and the arity of the function.

    See the following example.

    #include <iostream>
    #include <utility>
    #include <vector>
    #include <cassert>
    
    namespace util {
    template <typename ReturnType, typename... Args>
    struct function_traits_defs {
      static constexpr size_t arity = sizeof...(Args);
    
      using result_type = ReturnType;
    
      template <size_t i>
      struct arg {
        using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
      };
    };
    
    template <typename T>
    struct function_traits_impl;
    
    template <typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(Args...)>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(*)(Args...)>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...)>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) const>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) const&&>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) volatile&&>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits_impl<ReturnType(ClassType::*)(Args...) const volatile&&>
        : function_traits_defs<ReturnType, Args...> {};
    
    template <typename T, typename V = void>
    struct function_traits
        : function_traits_impl<T> {};
    
    template <typename T>
    struct function_traits<T, decltype((void)&T::operator())>
        : function_traits_impl<decltype(&T::operator())> {};
    
    template <size_t... Indices>
    struct indices {
      using next = indices<Indices..., sizeof...(Indices)>;
    };
    template <size_t N>
    struct build_indices {
      using type = typename build_indices<N - 1>::type::next;
    };
    template <>
    struct build_indices<0> {
      using type = indices<>;
    };
    template <size_t N>
    using BuildIndices = typename build_indices<N>::type;
    
    namespace details {
    template <typename FuncType,
              typename VecType,
              size_t... I,
              typename Traits = function_traits<FuncType>,
              typename ReturnT = typename Traits::result_type>
    ReturnT do_call(FuncType& func,
                    VecType& args,
               indices<I...> ) {
      assert(args.size() >= Traits::arity);
      return func(args[I]...);
    }
    }  // namespace details
    
    template <typename FuncType,
              typename VecType,
              typename Traits = function_traits<FuncType>,
              typename ReturnT = typename Traits::result_type>
    ReturnT unpack_caller(FuncType& func,
                    VecType& args) {
      return details::do_call(func, args, BuildIndices<Traits::arity>());
    }
    }  // namespace util
    
    int func(int a, int b, int c) {
      return a + b + c;
    }
    
    int main() {
      std::vector<int> args = {1, 2, 3};
    
      int j = util::unpack_caller(func, args);
      std::cout << j << std::endl;
    
      return 0;
    }
    
    0 讨论(0)
  • 2020-11-30 12:29

    You can use a pack of indices:

    template <size_t num_args>
    struct unpack_caller
    {
    private:
        template <typename FuncType, size_t... I>
        void call(FuncType &f, std::vector<int> &args, indices<I...>){
            f(args[I]...);
        }
    
    public:
        template <typename FuncType>
        void operator () (FuncType &f, std::vector<int> &args){
            assert(args.size() == num_args); // just to be sure
            call(f, args, BuildIndices<num_args>{});
        }
    };
    

    There's no way to remove the need to specify the size in the template though, because the size of a vector is a runtime construct, and we need the size at compile-time.

    0 讨论(0)
提交回复
热议问题