How do I expand a tuple into variadic template function's arguments?

前端 未结 13 1030
旧时难觅i
旧时难觅i 2020-11-22 07:49

Consider the case of a templated function with variadic template arguments:

template Tret func(const T&... t);
         


        
13条回答
  •  逝去的感伤
    2020-11-22 08:20

    All this implementations are good. But due to use of pointer to member function compiler often cannot inline the target function call (at least gcc 4.8 can't, no matter what Why gcc can't inline function pointers that can be determined?)

    But things changes if send pointer to member function as template arguments, not as function params:

    /// from https://stackoverflow.com/a/9288547/1559666
    template struct seq {};
    template struct gens : gens {};
    template struct gens<0, S...>{ typedef seq type; };
    
    template
    using makeSeq = typename gens< std::tuple_size< typename std::decay::type >::value >::type;
    
    
    // deduce function return type
    template
    struct fn_type;
    
    template
    struct fn_type< std::tuple >{
    
        // will not be called
        template
        static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval()...)){
            //return (self.*f)(Args()...);
            return NULL;
        }
    };
    
    template
    struct APPLY_TUPLE{};
    
    template
    struct APPLY_TUPLE>{
        Self &self;
        APPLY_TUPLE(Self &self): self(self){}
    
        template
        void delayed_call(Tuple &&list){
            caller(forward(list), makeSeq() );
        }
    
        template
        void caller(Tuple &&list, const seq){
            (self.*f)( std::get(forward(list))... );
        }
    };
    
    #define type_of(val) typename decay::type
    
    #define apply_tuple(obj, fname, tuple) \
        APPLY_TUPLE::type, typename decay::type >(obj).delayed_call< \
                decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay::type::fname) ), \
                &decay::type::fname \
                > \
                (tuple);
    

    And ussage:

    struct DelayedCall
    {  
        void call_me(int a, int b, int c){
            std::cout << a+b+c;
        }
    
        void fire(){
            tuple list = make_tuple(1,2,3);
            apply_tuple(*this, call_me, list); // even simpler than previous implementations
        }
    };
    

    Proof of inlinable http://goo.gl/5UqVnC


    With small changes, we can "overload" apply_tuple:

    #define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
    #define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
    #define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__)
    #define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__)
    #define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
    
    #define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple)
    #define apply_tuple3(obj, fname, tuple) \
        APPLY_TUPLE::type, typename decay::type >(obj).delayed_call< \
                decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay::type::fname) ), \
                &decay::type::fname \
                /* ,decltype(tuple) */> \
                (tuple);
    #define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__)
    
    ...
    
    apply_tuple(obj, call_me, list);
    apply_tuple(call_me, list);       // call this->call_me(list....)
    

    Plus this is the only one solution which works with templated functions.

提交回复
热议问题