#c #templates
#c #шаблоны
Вопрос:
В настоящее время я использую перечисление для отображения в массив 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? Или есть лучший способ реализовать этот тип шаблона? Кроме того, было бы неплохо, чтобы каждый производный тип добавлял себя к любой структуре данных, присваивающей ему значение индекса.
В принципе, мне нужен статический контейнер разнородных указателей, к которому можно получить доступ по типу или индексу, при этом возвращая правильный производный * . Я предполагаю, что в Boost есть что-то, что могло бы сработать, но я пока этого не нашел.
Спасибо за любую помощь.
Ответ №1:
Хотя я не уверен на 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 ) = amp;d1; // access with type
bf::at_key< Derived2 >( c ) = amp;d2;
// access with enum
bf::at_key< bm::at_c< DerivedTypes, DERIVED_TYPE_1 >::type >( c ) = amp;d1;
bf::at_key< bm::at_c< DerivedTypes, DERIVED_TYPE_2 >::type >( c ) = amp;d2;
}
Комментарии:
1. Отлично, именно то, что я искал, спасибо. Одно из внесенных мной улучшений заключается в автоматическом заполнении карты из вектора:
typedef bm::fold<DerivedTypes, bf::map<>, bm::push_back<bm::_1, bf::pair<bm::_2, boost::add_pointer<bm::_2> > > >::type Container;
Ответ №2:
(1) is there a way to have the "non-template" getOb(DerivedType type) return the correct DerivedN pointer
К сожалению, это невозможно. Для не шаблонных функций возвращаемый тип может быть только одного типа, и в вашем случае это должен быть Base*
type. Текущая реализация 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.
};
Ответ №3:
Ну, то, что у вас сейчас есть, выглядит для меня довольно солидно. Единственная проблема, которую я вижу, заключается в том, что у вас будет только 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:
GetObClassamp; 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);