Контейнер гетерогенного указателя - PullRequest
1 голос
/ 09 мая 2011

В настоящее время я использую enum для отображения в массив Base *. Каждому производному типу присваивается индекс по перечислению.

enum DerivedType {
    DERIVED_TYPE_1 = 0,
    DERIVED_TYPE_2,
    ...
    NUM_DERIVED_TYPES
};


class Base {

};


class Derived1 : public Base {
    static const DerivedType type;
};
const DerivedType Derived1::type = DERIVED_TYPE_1;


class Derived2 : public Base {
    static const DerivedType type;
};
const DerivedType Derived2::type = DERIVED_TYPE_2;


class Container {
    Base* obs[NUM_DERIVED_TYPES];

    template<class T>
    void addOb(T* ob) {
        obs[T::type] = ob;
    }

    template<class T>
    T* getOb() {
        return (T*) obs[T::type];
    }

    Base* getOb(DerivedType type) {
        return obs[type];
    }
};

Поскольку индекс каждого производного типа известен во время компиляции, есть ли способ заставить не-шаблон getOb(DerivedType type) вернуть правильный указатель DerivedN, может быть, путем поиска имени типа в карте int -> typename? Или есть лучший способ реализовать этот тип шаблона? Кроме того, было бы неплохо, чтобы каждый тип Derived добавлялся в любую структуру данных, присваивающую ему значение индекса.

По сути, мне нужен контейнер статического гетерогенного указателя, к которому можно получить доступ по типу или по индексу, при этом возвращая правильный Derived *. Я предполагаю, что у Boost есть что-то, что будет работать, но я еще не нашел его.

Спасибо за любую помощь.

Ответы [ 3 ]

2 голосов
/ 09 мая 2011

Хотя я не уверен на 100%, что правильно понял вопрос, возможно, то, что вы упомянули (или аналогичный), может быть реализовано с помощью boost::fusion и boost::mpl.
Например:

#include <boost/fusion/include/map.hpp>
#include <boost/fusion/include/at_key.hpp>
#include <boost/mpl/vector.hpp>
namespace bf = boost::fusion;
namespace bm = boost::mpl;

// This order has to match with the enumerators in DerivedType
typedef bm::vector< Derived1, Derived2 > DerivedTypes;

typedef bf::map< bf::pair< Derived1, Derived1* >
               , bf::pair< Derived2, Derived2* > > Container;

int main() {
    Container c( bf::make_pair< Derived1, Derived1* >(0)
               , bf::make_pair< Derived2, Derived2* >(0) );
    Derived1 d1;
    Derived2 d2;
    bf::at_key< Derived1 >( c ) = &d1; // access with type
    bf::at_key< Derived2 >( c ) = &d2;
    // access with enum
    bf::at_key< bm::at_c< DerivedTypes, DERIVED_TYPE_1 >::type >( c ) = &d1;
    bf::at_key< bm::at_c< DerivedTypes, DERIVED_TYPE_2 >::type >( c ) = &d2;
}
0 голосов
/ 09 мая 2011

Ну, то, что у вас сейчас есть, выглядит довольно солидно для меня. Единственная проблема, которую я вижу, заключается в том, что у вас будет только Base*, доступный во время выполнения, поскольку одна функция может возвращать только 1 тип. Чтобы разрешить многостраничный возврат без указания дополнительного параметра, вы можете подделать функцию следующим образом:

// if employed as a free function
class getOb{
  DerivedType _type;
public:
  getOb(DerivedType type)
    : _type(type) {}

  template<class T>
  operator T*() const{
    Base* ptr;
    // fetch correct pointer from wherever
    // using _type, if non-existant use 0
    return (T*) ptr;
  }
};

Использование как

Derived1* pd = getOb(DERIVED_TYPE_1);
assert(pd != 0);

Параметр передается в конструктор, а фактическое тело функции лежит в операторе преобразования. Конечно, это выглядит хорошо только как отдельно стоящая функция, поэтому вот как это будет выглядеть, если вы захотите реализовать ее как функцию-член

class GetObClass{
  mutable DerivedType _type;
public:
  GetObClass& operator()(DerivedType type) const{
    _type = type;
    return *this;
  }

  // template conversion operator as before
};

Использование как

class Container{
public:
  const GetObClass getOb;
};

Container c;
Derived1* pd = c.getOb(DERIVED_TYPE_1);
assert(pd != 0);
0 голосов
/ 09 мая 2011

(1) is there a way to have the "non-template" getOb(DerivedType type) return the correct DerivedN pointer

К сожалению, это невозможно.Для не шаблонных функций тип возвращаемого значения может быть только одного типа, и в вашем случае это должен быть тип Base*.Текущая реализация Base* getOb(DerivedType type); правильная.

(2) is there a better way to implement this type of pattern?

По крайней мере, вы можете облегчить свою работу, используя шаблонизированный промежуточный класс (без каких-либо накладных расходов), который инициализирует DerivedType переменная для вас.Например, объявите что-то вроде следующего:

template<DerivedType TYPE>
struct Link : Base {
  static const DerivedType type;
};
template<DerivedType TYPE>
const DerivedType Link<TYPE>::type = TYPE;

Теперь Link<DerivedType> должно быть унаследовано для производных классов, например:

class Derived1 : public Link<DERIVED_TYPE_1> {
// no need to declare/define an explicit variable for DerivedType now.
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...