C++0x: How can I access variadic tuple members by index at runtime?

后端 未结 4 668
既然无缘
既然无缘 2021-01-14 07:38

I have written the following basic Tuple template:

template 
class Tuple;

template 
struct TupleIndex         


        
4条回答
  •  攒了一身酷
    2021-01-14 07:59

    Do something like this:

    namespace detail
    {
        template 
        R select(Tuple&& pTuple, Func pFunc)
        {
            return pFunc(get(std::forward(pTuple)));
        }
    
        template 
        R select_element(Tuple&& pTuple, std::size_t pIndex, Func pFunc)
        {
            if (pIndex == I)
                return select(std::forward(pTuple), pFunc);
            else
                return select(std::forward(pTuple), pIndex, pFunc);
        }
    }
    
    template 
    R select(Tuple&& pTuple, std::size_t pIndex, Func pFunc)
    {
        typedef typename std::remove_reference::type tuple_type;
    
        // assumes all possible calls to Func return the same type
        typedef typename std::tuple_element<0, tuple_type>::type dummy_type;
        typedef typename std::result_of::type result_type;
    
        if (pIndex >= std::tuple_size::value)
            throw std::out_of_range("select out of range");
    
        return detail::select<0, result_type>(
                                        std::forward(pTuple), pIndex, pFunc);
    }
    

    This lets you call a functor with a run-time selected element, by checking each index incrementally. It returns whatever the function call returns, but it assumes that all invocations result in the same type. (Though right now, it'll "work" as long as all invocations happen to be implicitly convertible to the same type as an invocation of the first element. You can assert they all match if you want to, but that's outside the scope of this question.)

    I'd be surprised if the compiler didn't unroll it, but I don't know for certain. In any case, it's simple and works (well, untested, but I assume it does) and that's far more important.

    So whatever you wanted to do with your run-time selected element, operate on it with this. You can make the call templated:

    struct print_element
    {
        // T is determined at compile time for each possible element type,
        // but which overload gets selected is determined at run-time
        template 
        void operator()(const T& pX) const
        {
            std::cout << pX << std::endl;
        }
    };
    

    If you really just want the value as some type, then you can make a simple functor:

    namespace detail
    {
        template 
        struct get_element
        {
            template 
            R operator()(T&& pValue) const
            {
                return std::forward(pValue);
            }
        };
    }
    
    template 
    R get(Tuple&& pTuple, std::size_t pIndex)
    {
       return select(std::forward(pTuple), pIndex, get_element());
    }
    

    You can use it like this:

    auto x = get(myTuple, i);
    

    To get void*'s (yuck), you need one last simple utility (too bad we don't get polymorphic lambda's):

    class get_address
    {
    public:
        template 
        get_address(T& pValue) :
        mResult(&pValue)
        {}
    
        void* get() const
        {
            return mResult;
        }
    
        operator void*() const
        {
            return get();
        }
    
    private:
        void* mResult;
    };
    

    Allowing:

    void* addr = get(myTuple, i);
    

提交回复
热议问题