Home > Mobile >  How to pass std::index_sequence param to an nested-template struct according to function traits
How to pass std::index_sequence param to an nested-template struct according to function traits

Time:07-27

Here is my simple code and it did work (I passed single integer to argument):

#include <iostream>
#include <tuple>
#include <string>
#include "boost/variant.hpp"

using TObjList = std::vector<boost::variant<std::string, int>>;

template<typename> struct FTrait;
template<typename R, typename... A> struct FTrait<R(A...)> {
    using RetType = R;
    using ArgsTuple = std::tuple<A...>;
    static constexpr std::size_t arity = sizeof...(A);

    template<size_t N> struct argument {
        static_assert( N < arity , "error: invalid argument index");
        typedef typename std::tuple_element_t<N, std::tuple<A...>> type;
    };
};
void Show(const std::string& s, int a) {
    std::cout << s << "\n" << a << "\n";
}

int main() {
    TObjList list;
    list.emplace_back("hello");
    list.emplace_back(567);
    Show(boost::get<FTrait<decltype(Show)>::argument<0>::type>(list[0]),
         boost::get<FTrait<decltype(Show)>::argument<1>::type>(list[1]));
}

But the following code did not work (Maybe I passed a sequence to argument):

#include <iostream>
#include <tuple>
#include <string>
#include "boost/variant.hpp"

using TObjList = std::vector<boost::variant<std::string, int>>;

template<typename> struct FTrait;
template<typename R, typename... A> struct FTrait<R(A...)> {
    using RetType = R;
    using ArgsTuple = std::tuple<A...>;
    static constexpr std::size_t arity = sizeof...(A);

    template<size_t N> struct argument {
        static_assert( N < arity , "error: invalid argument index");
        typedef typename std::tuple_element_t<N, std::tuple<A...>> type;
    };
};

// here is new code
template<typename T> struct ConvertOne {
    T operator () (const TObjList& List, size_t Index) {
        if ( Index < List.size() ) {
            return boost::get<T>(List[Index]);
        }
    }
};

template<typename F, size_t... Index>
void MyCall(const F& Func, const TObjList& List, const std::index_sequence<Index...>&) {
    // This code not work:
    Func(ConvertOne<typename FTrait<F>::argument<Index>::type>{}(List, Index)...);

    // These code would work:
    // using ArgsTuple = typename FTrait<F>::ArgsTuple;
    // Func(ConvertOne<std::decay_t<typename std::tuple_element_t<Index, ArgsTuple>>>{}(List, Index)...);
}
// new code done

void Show(const std::string& s, int a) {
    std::cout << s << "\n" << a << "\n";
}

int main() {
    TObjList list;
    list.emplace_back("hello");
    list.emplace_back(567);
    // new invoke
    MyCall(Show, list, std::make_index_sequence<FTrait<decltype(Show)>::arity>{});
}

This code would raise a compile error:

ftrait.cpp: In function ‘void MyCall(const F&, const TObjList&, std::index_sequence<Index ...>&)’:
ftrait.cpp:34:62: error: template argument 1 is invalid
     Func(ConvertOne<typename FTrait<F>::argument<Index>::type>{}(List, Index)...);

I hope to use std::index_sequence and function_traits to make the program automatically identify the type and number of parameters of a function, like std::tuple_element<Index, ArgsTuple>::type. boost::function_traits cannot satisfy me, so I try to implement by myself. What should I do? Thanks :)

CodePudding user response:

In your case, typename FTrait<F>::argument<0>::type is const std::string&.

  • std::get<const std::string&>(list[0]) won't compile
  • you need std::get<std::string>(list[0]).

It might be solved with std::decay:

template<typename F, size_t... Index>
void MyCall(const F& Func, const TObjList& List, const std::index_sequence<Index...>&)
{
    Func(ConvertOne<
             typename std::decay<
                 typename FTrait<F>::template argument<Index>::type
             >::type
         >{}(List, Index)...);
}

Demo

  • Related