C ++ 0x:如何在运行时通过索引访问可变参数元组成员? [英] C++0x: How can I access variadic tuple members by index at runtime?

查看:111
本文介绍了C ++ 0x:如何在运行时通过索引访问可变参数元组成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了以下基本元组模板:

  template< typename ... T> 
class Tuple;

模板< uintptr_t N,typename ... T>
struct TupleIndexer;

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

private:
主要元素;

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

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

私人:
朋友结构TupleIndexer< 0,Head,Tail ...> ;;

};

模板<>
class Tuple<> {

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

};

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

typedef Head&类型;

静态类型Get(元组<头部,尾部......>&元组){
return tuple.element;
}

};

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

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

静态类型Get(元组<头部,尾部......>&元组){
返回TupleIndexer< N - 1,Tail ...> :: Get(*( Tuple< Tail ...> *)& tuple);
}

};

它工作正常,我可以使用<$ c $以类似数组的方式访问元素c> tuple.Get< Index >() - 但如果我在编译时知道索引,我只能这样做。但是,我需要在运行时通过索引访问元组中的元素,并且我不知道在编译时需要访问哪个索引。示例:

  int chosenIndex = getUserInput(); 
void * chosenElement = tuple.Get(chosenIndex);
cout<< 你选择的选项是:<< ((MyAbstractBaseClass *)chosenElement) - > getInfo()<< ENDL;

最好的方法是什么?



编辑:



以下黑客解决方案:



<好的,我有个主意。在我发布这个问题之前,我已经找到了一种方法,但它是hackish并产生了警告。由于另一种解决方案不会立即出现,也许你们可以帮助我改善我的hackish。 : - )



通常不能像数组一样访问元组,因为元素不一定都是相同的大小。 (因此,在类结构中获得正确的偏移量的数组样式乘法将无济于事。)但是,我设法通过创建一个包含元组偏移列表的静态表来解决这个问题。这是完整的元组和相关模板:

  #include< cstddef> 

模板< typename ... T>
class Tuple;

模板< uintptr_t N,typename ... T>
struct TupleIndexer;

模板< 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 ...>类型;

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

}

模板<>
struct TupleOffsets<> {

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

};

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

private:
主要元素;

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

模板< uintptr_t N>
typename TupleIndexer< N,Head,Tail ...> :: Type& Get(){
返回TupleIndexer< N,Head,Tail ...> :: Get(* this);
}

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

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

朋友结构TupleOffsets< Head,Tail ...> ;;
朋友结构TupleIndexer< 0,Head,Tail ...> ;;

};

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

模板<>
class Tuple<> {

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

};

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

typedef Head&类型;

静态类型Get(元组<头部,尾部......>&元组){
return tuple.element;
}

};

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

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

静态类型Get(元组<头部,尾部......>&元组){
返回TupleIndexer< N - 1,Tail ...> :: Get(*( Tuple< Tail ...> *)& tuple);
}

};

在实践中它有效。但是,编译器给出了在非POD数据类型上使用offsetof的警告,我不确定这个解决方案是多么可移植。任何人都知道如何改进这个解决方案吗?

解决方案

我很难绕过我找到的解决方案,所以我塑造了自己的一个。我的元组中的所有成员都来自同一个类,所以我通过在我的元组类中添加一个基类型参数并使用指向成员的指针来调整我以前的解决方案:

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

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

模板< typename Base,typename ... T>
struct TupleOffsets;

模板< 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]; }

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

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

};

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

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

}

模板< typename Base>
struct TupleOffsets< Base> {

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

};

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

private:
主要元素;

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

模板< uintptr_t N>
typename TupleIndexer< Base,N,Head,Tail ...> :: Type& Get(){
返回TupleIndexer< Base,N,Head,Tail ...> :: Get(* this);
}

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

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

朋友结构TupleOffsets< Base,Head,Tail ...> ;;
friend struct TupleIndexer< Base,0,Head,Tail ...> ;;

};

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

模板< typename Base>
class Tuple< Base> {

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

};

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

typedef Head&类型;

静态类型Get(元组< Base,Head,Tail ...>& tuple){
return tuple.element;
}

};

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

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

静态类型Get(元组< Base,Head,Tail ...>& tuple){
返回TupleIndexer< Base,N - 1,Tail ...> ::得到(*(元组< Base,Tail ...> *)& tuple);
}

};

以下现在效果很好,这是我最终拍摄的:

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

struct Derived1:public Base {
virtual void print(){cout<< 我是第一个派生阶级! << ENDL; }
};

struct Derived2:public Base {
virtual void print(){cout<< 哇哦!我是第二个派生阶级! << ENDL; }
};

...

元组< Base,Derived1,Derived2> VAR;
var.Get(0) - > print();
var.Get(1) - > print();


I have written 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 just fine, and I can access elements in array-like fashion by using tuple.Get<Index>() - but I can only do that if I know the index at compile-time. However, I need to access elements in the tuple by index at runtime, and I won't know at compile-time which index needs to be accessed. Example:

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

What's the best way to do this?

EDIT:

Hackish solution below:

Okay, I've got an idea. I already figured out one way of doing this before I even posted this question, but it was hackish and produced warnings. Since another solution isn't forthcoming right away, maybe you guys could help me improve my hackish one. :-)

The tuple can't normally be accessed like an array because the elements are not all necessarily of the same size. (Hence array-style multiplication to arrive at the correct offset in the class structure will not help.) However, I managed to work around this by creating a static table that contains a list of offsets for a tuple. Here's the complete tuple and related templates:

#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);
    }

};

In practice it works. However, the compiler gives me a warning for using offsetof on a non-POD data type, and I'm not sure how portable this solution is. Anyone know how I might improve this solution?

解决方案

I was having a hard time wrapping my head around the solutions I was finding, so I fashioned one of my own. All of the members in my tuple derive from the same class, so I adapted my previous solution by adding a base type parameter to my tuple class and using pointers-to-members:

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);
    }

};

The following now works nicely, which is what I was ultimately shooting for:

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();

这篇关于C ++ 0x:如何在运行时通过索引访问可变参数元组成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆