C ++ 0x: How can I access variable set members by index at runtime?

I wrote the following basic Tuple template:

template <typename... T>
class Tuple;

template <uintptr_t N, typename... T>
struct TupleIndexer;

template <typename Head, typename... Tail>
class Tuple<Head, Tail...> : public Tuple<Tail...> {

    private:
    Head element;

    public:
    template <uintptr_t N>
    typename TupleIndexer<N, Head, Tail...>::Type& Get() {
        return TupleIndexer<N, Head, Tail...>::Get(*this);
    }

    uintptr_t GetCount() const {
        return sizeof...(Tail) + 1;
    }

    private:
    friend struct TupleIndexer<0, Head, Tail...>;

};

template <>
class Tuple<> {

    public:
    uintptr_t GetCount() const {
        return 0;
    }

};

template <typename Head, typename... Tail>
struct TupleIndexer<0, Head, Tail...> {

    typedef Head& Type;

    static Type Get(Tuple<Head, Tail...>& tuple) {
        return tuple.element;
    }

};

template <uintptr_t N, typename Head, typename... Tail>
struct TupleIndexer<N, Head, Tail...> {

    typedef typename TupleIndexer<N - 1, Tail...>::Type Type;

    static Type Get(Tuple<Head, Tail...>& tuple) {
        return TupleIndexer<N - 1, Tail...>::Get(*(Tuple<Tail...>*) &tuple);
    }

};

It works fine, and I can access the elements as an array using tuple.Get<Index>()- but I can only do this if I know the index at compile time. However, I need to access the elements in the tuple by index at runtime, and at compile time I will not know which index to access. Example:

int chosenIndex = getUserInput();
void* chosenElement = tuple.Get(chosenIndex);
cout << "The option you chose was: " << ((MyAbstractBaseClass*) chosenElement)->getInfo() << endl;

What is the best way to do this?

EDIT:

Hacking solution below:

Ok, I have an idea. I already figured out one way to do this before I even posted this question, but it was hacked and triggered warnings. Since the other solution is not right away, maybe you guys could help me improve my hacker approach. :-)

, , . (, , , .) , , . :

#include <cstddef>

template <typename... T>
class Tuple;

template <uintptr_t N, typename... T>
struct TupleIndexer;

template <typename... T>
struct TupleOffsets;

template <typename Head, typename... Tail>
struct TupleOffsets<Head, Tail...> {

    TupleOffsets() { Init(offsets); }
    static void Init(uintptr_t* offsets);
    uintptr_t const& operator[] (uintptr_t i) const { return offsets[i]; }

    private:
    uintptr_t offsets[sizeof...(Tail) + 1];

};

template <typename Head, typename... Tail>
void TupleOffsets<Head, Tail...>::Init(uintptr_t* offsets) {

    typedef Tuple<Head, Tail...> Type;

    *offsets = offsetof(Type, element);
    TupleOffsets<Tail...>::Init(++offsets);

}

template <>
struct TupleOffsets<> {

    TupleOffsets() {}
    static void Init(uintptr_t* offsets) {}

};

template <typename Head, typename... Tail>
class Tuple<Head, Tail...> : public Tuple<Tail...> {

    private:
    Head element;

    public:
    void* Get(uintptr_t i) {
        return (uint8_t*) this + offsets[i];
    }

    template <uintptr_t N>
    typename TupleIndexer<N, Head, Tail...>::Type& Get() {
        return TupleIndexer<N, Head, Tail...>::Get(*this);
    }

    uintptr_t GetCount() const {
        return sizeof...(Tail) + 1;
    }

    private:
    static const TupleOffsets<Head, Tail...> offsets;

    friend struct TupleOffsets<Head, Tail...>;
    friend struct TupleIndexer<0, Head, Tail...>;

};

template <typename Head, typename... Tail>
const TupleOffsets<Head, Tail...> Tuple<Head, Tail...>::offsets;

template <>
class Tuple<> {

    public:
    uintptr_t GetCount() const {
        return 0;
    }

};

template <typename Head, typename... Tail>
struct TupleIndexer<0, Head, Tail...> {

    typedef Head& Type;

    static Type Get(Tuple<Head, Tail...>& tuple) {
        return tuple.element;
    }

};

template <uintptr_t N, typename Head, typename... Tail>
struct TupleIndexer<N, Head, Tail...> {

    typedef typename TupleIndexer<N - 1, Tail...>::Type Type;

    static Type Get(Tuple<Head, Tail...>& tuple) {
        return TupleIndexer<N - 1, Tail...>::Get(*(Tuple<Tail...>*) &tuple);
    }

};

. , offsetof , POD, , . - , ?

+3
4

, , . , , :

template <typename Base, typename... T>
class Tuple;

template <typename Base, uintptr_t N, typename... T>
struct TupleIndexer;

template <typename Base, typename... T>
struct TupleOffsets;

template <typename Base, typename Head, typename... Tail>
struct TupleOffsets<Base, Head, Tail...> {

    TupleOffsets() { Init<Base Tuple<Base, Head, Tail...>::*>(offsets); }
    Base Tuple<Base, Head, Tail...>::* const& operator[] (uintptr_t i) const { return offsets[i]; }

    template <typename PtrType>
    static void Init(PtrType* offsets);

    private:
    Base Tuple<Base, Head, Tail...>::* offsets[sizeof...(Tail) + 1];

};

template <typename Base, typename Head, typename... Tail>
template <typename PtrType>
void TupleOffsets<Base, Head, Tail...>::Init(PtrType* offsets) {

    *offsets = PtrType(&Tuple<Base, Head, Tail...>::element);
    TupleOffsets<Base, Tail...>::Init(++offsets);

}

template <typename Base>
struct TupleOffsets<Base> {

    TupleOffsets() {}
    template <typename PtrType>
    static void Init(PtrType* offsets) {}

};

template <typename Base, typename Head, typename... Tail>
class Tuple<Base, Head, Tail...> : public Tuple<Base, Tail...> {

    private:
    Head element;

    public:
    Base* Get(uintptr_t i) {
        return &(this->*offsets[i]);
    }

    template <uintptr_t N>
    typename TupleIndexer<Base, N, Head, Tail...>::Type& Get() {
        return TupleIndexer<Base, N, Head, Tail...>::Get(*this);
    }

    uintptr_t GetCount() const {
        return sizeof...(Tail) + 1;
    }

    private:
    static const TupleOffsets<Base, Head, Tail...> offsets;

    friend struct TupleOffsets<Base, Head, Tail...>;
    friend struct TupleIndexer<Base, 0, Head, Tail...>;

};

template <typename Base, typename Head, typename... Tail>
const TupleOffsets<Base, Head, Tail...> Tuple<Base, Head, Tail...>::offsets;

template <typename Base>
class Tuple<Base> {

    public:
    uintptr_t GetCount() const {
        return 0;
    }

};

template <typename Base, typename Head, typename... Tail>
struct TupleIndexer<Base, 0, Head, Tail...> {

    typedef Head& Type;

    static Type Get(Tuple<Base, Head, Tail...>& tuple) {
        return tuple.element;
    }

};

template <typename Base, uintptr_t N, typename Head, typename... Tail>
struct TupleIndexer<Base, N, Head, Tail...> {

    typedef typename TupleIndexer<Base, N - 1, Tail...>::Type Type;

    static Type Get(Tuple<Base, Head, Tail...>& tuple) {
        return TupleIndexer<Base, N - 1, Tail...>::Get(*(Tuple<Base, Tail...>*) &tuple);
    }

};

, , :

struct Base {
    virtual void print() = 0;
};

struct Derived1 : public Base {
    virtual void print() { cout << "I'm the first derived class!" << endl; }
};

struct Derived2 : public Base {
    virtual void print() { cout << "Woohoo!  I'm the second derived class!" << endl; }
};

...

Tuple<Base, Derived1, Derived2> var;
var.Get(0)->print();
var.Get(1)->print();
+1

- :

namespace detail
{
    template <std::size_t I, typename R, typename Tuple, typename Func>
    R select(Tuple&& pTuple, Func pFunc)
    {
        return pFunc(get<I>(std::forward<Tuple>(pTuple)));
    }

    template <std::size_t I, typename R, typename Tuple, typename Func>
    R select_element(Tuple&& pTuple, std::size_t pIndex, Func pFunc)
    {
        if (pIndex == I)
            return select<I, R>(std::forward<Tuple>(pTuple), pFunc);
        else
            return select<I + 1, R>(std::forward<Tuple>(pTuple), pIndex, pFunc);
    }
}

template <typename Tuple, typename Func>
R select(Tuple&& pTuple, std::size_t pIndex, Func pFunc)
{
    typedef typename std::remove_reference<Tuple>::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<Func, dummy_type>::type result_type;

    if (pIndex >= std::tuple_size<tuple_type>::value)
        throw std::out_of_range("select out of range");

    return detail::select<0, result_type>(
                                    std::forward<Tuple>(pTuple), pIndex, pFunc);
}

, . , , , . ( "", , . , , , .)

, , . , (, , , ) .

, , , . :

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 <typename T>
    void operator()(const T& pX) const
    {
        std::cout << pX << std::endl;
    }
};

, - , :

namespace detail
{
    template <typename R>
    struct get_element
    {
        template <typename T>
        R operator()(T&& pValue) const
        {
            return std::forward<T>(pValue);
        }
    };
}

template <typename R, typename Tuple>
R get(Tuple&& pTuple, std::size_t pIndex)
{
   return select(std::forward<Tuple>(pTuple), pIndex, get_element<R>());
}

:

auto x = get<boost::any>(myTuple, i);

void* (yuck), ( , ):

class get_address
{
public:
    template <typename T>
    get_address(T& pValue) :
    mResult(&pValue)
    {}

    void* get() const
    {
        return mResult;
    }

    operator void*() const
    {
        return get();
    }

private:
    void* mResult;
};

:

void* addr = get<get_address>(myTuple, i);
+1

, std::tuple?

-, tuple , , .

, boost::any.

0

, TupleIndexer, .

​​ Tuple:

Head &operator[](unsigned i) {
    return i ? ((Tuple<Tail...>&)*this)[i-1] : element;
}

and add the specialization for Tuple <Head>:

Head &operator[](unsigned i) {
    assert(!i);
    return i;
}

(You cannot put the base register in Tuple <gt; because you do not have a return type that will be compatible with all possible callers.)

-1
source

Source: https://habr.com/ru/post/1781383/


All Articles