Более общий шаблон посетителей

Извините, если мой вопрос такой длинный и технический, но я думаю, что это так важно, что другие люди будут заинтересованы в этом

Я искал способ четко отделить некоторые программные компоненты от их представления в С ++

У меня есть универсальный класс параметров (который будет позже сохранен в контейнере), который может содержать любое значение с классом boost::any

У меня есть базовый класс (примерно) такого рода (конечно, есть больше вещей)

class Parameter 
{
public:
    Parameter()
    template typename<T> T GetValue() const { return any_cast<T>( _value ); }
    template typename<T> void SetValue(const T& value) { _value = value; }
    string GetValueAsString() const = 0;
    void SetValueFromString(const string& str) const = 0;
private:
    boost::any _value;
}

Существует два уровня производных классов: первый уровень определяет тип и преобразование в / из строки (например, ParameterInt или ParameterString); второй уровень определяет поведение и реальных создателей (например, получение ParameterAnyInt и ParameterLimitedInt из ParameterInt или ParameterFilename из GenericString)

В зависимости от реального типа я хотел бы добавить внешнюю функцию или классы, которые работают в зависимости от конкретного типа параметра, без добавления виртуальных методов в базовый класс и без выполнения странных приведений.

Например, я хотел бы создать соответствующие элементы управления графическим интерфейсом в зависимости от типов параметров:

Widget* CreateWidget(const Parameter& p)

Конечно, я не могу понять настоящий тип параметра из этого, если я не использую RTTI или не использую его сам (с перечислением и регистром переключателя), но это не правильное проектное решение ООП, вы знаете.

Классическим решением является шаблон проектирования Visitor http://en.wikipedia.org/wiki/Visitor_pattern

Проблема с этим шаблоном заключается в том, что мне нужно заранее знать, какие производные типы будут реализованы, поэтому (собрав воедино то, что написано в википедии и моем коде), у нас будет что-то вроде:

struct Visitor 
{
  virtual void visit(ParameterLimitedInt& wheel) = 0;
  virtual void visit(ParameterAnyInt& engine) = 0;
  virtual void visit(ParameterFilename& body) = 0;
};

Есть ли какое-либо решение для получения такого поведения любым другим способом без необходимости заранее знать все конкретные типы и без извлечения первоначального посетителя?


Редактировать: решение доктора Пиццы кажется наиболее близким к тому, о чем я думал, но проблема все та же, и метод фактически полагается на dynamic_cast, которого я пытался избежать как своего рода (даже если слабый) метод RTTI

Может быть, лучше подумать о каком-то решении, даже не процитировав шаблон посетителей, и очистить наш разум. Цель состоит в том, чтобы просто иметь такую ​​функцию:

Widget* CreateWidget(const Parameter& p)

ведут себя по-разному для каждого "конкретного" параметра, не теряя информацию о его типе

5 ответов

Для общей реализации Vistor я бы предложил Loki Visitor, часть библиотеки Loki.

Я использовал это ("ациклический посетитель") для хорошего эффекта; это делает возможным добавление новых классов в иерархию, в некоторой степени не изменяя существующие.

Если я правильно понимаю...

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

Для полноты картины:

Конечно, вполне возможно написать собственную реализацию таблицы мультиметодных указателей для ваших объектов и вычислить адреса методов вручную во время выполнения. Есть статья Страуструпа на тему реализации мультиметодов (хотя и в компиляторе).

Я бы никому не советовал делать это. Добиться, чтобы реализация работала хорошо, довольно сложно, и синтаксис ее использования, вероятно, будет очень неудобным и подверженным ошибкам. Если все остальное терпит неудачу, это могло бы все еще быть способом пойти, все же.

У меня проблемы с пониманием ваших требований. Но я скажу - как бы своими словами - как я понимаю ситуацию:

  • У вас есть абстрактный класс Parameter, который в конце концов подклассируется для некоторых конкретных классов (например: ParameterLimitedInt).

  • У вас есть отдельная система графического интерфейса пользователя, которая будет передавать эти параметры в общем виде, но выгода заключается в том, что она должна представлять компонент графического интерфейса, специфичный для конкретного типа класса параметров.

  • Ограничения заключаются в том, что вы не хотите использовать RTTID и не хотите писать код для обработки каждого возможного типа конкретного параметра.

  • Вы открыты для использования шаблона посетителя.

С учетом ваших требований, вот как я бы справился с такой ситуацией:

Я хотел бы реализовать шаблон посетителя, где accept() возвращает логическое значение. Базовый класс Parameter реализует виртуальную функцию accept() и возвращает false.

Конкретные реализации класса Parameter будут содержать функции accept(), которые будут вызывать визит посетителя (). Они вернут истину.

Класс посетителя будет использовать шаблонную функцию visit(), поэтому вы можете переопределять только те конкретные типы параметров, которые вы хотите поддерживать:

class Visitor
{
public:
  template< class T > void visit( const T& param ) const
  {
    assert( false && "this parameter type not specialised in the visitor" );
  }
  void visit( const ParameterLimitedInt& ) const; // specialised implementations...
}

Таким образом, если accept() возвращает false, вы знаете, что конкретный тип для параметра еще не реализовал шаблон посетителя (в случае, если есть дополнительная логика, которую вы бы предпочли обрабатывать в каждом конкретном случае). Если assert() в шаблоне посетителя срабатывает, то это потому, что он не посещает тип параметра, для которого вы реализовали специализацию.

Одним из недостатков всего этого является то, что неподдерживаемые посещения обнаруживаются только во время выполнения.

Другие вопросы по тегам