Извините, если мой вопрос будет таким длинным и техническим, но я думаю, что он настолько важен, что другим людям он будет интересен
Я искал способ четко отделить некоторые внутренние компоненты программного обеспечения от их представления на C++.
У меня есть общий класс параметров (который позже будет сохранен в контейнере), который может содержать любое значение с классом 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)
вести себя по-разному для каждого "конкретного" параметра без потери информации о его типе





Если я правильно понимаю ...
У нас был объект, который мог использовать разные варианты оборудования. Чтобы облегчить это, мы использовали абстрактный интерфейс Device. В устройстве был набор функций, которые запускались при определенных событиях. Использование будет таким же, но различные реализации Устройства будут либо иметь полностью реализованные функции, либо просто возвращаться немедленно. Чтобы сделать жизнь еще проще, функции были аннулированы и выдавали исключения, когда что-то пошло не так.
Я использовал это («ациклический посетитель») для хорошего эффекта; это делает возможным добавление новых классов в иерархию без изменения в некоторой степени существующих.
@Nicole web.archive.org/web/20080517124442/http://www.objectmentor.c om /…
Для полноты картины:
конечно, вполне возможно написать собственную реализацию таблицы мультиметодных указателей для ваших объектов и вычислить адреса методов вручную во время выполнения. Есть бумага Страуструпа по теме реализации мультиметодов (хотя и в компиляторе).
Я бы никому не советовал это делать. Обеспечить хорошую работу реализации довольно сложно, и синтаксис для ее использования, вероятно, будет очень неудобным и подверженным ошибкам. Если все остальное не удается, это все равно может быть подходящим вариантом.
Мне сложно понять ваши требования. Но я скажу - как бы своими словами - то, что я понимаю в этой ситуации:
У вас есть абстрактный класс 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 () в шаблоне посетителя, это потому, что он не обращается к типу параметра, для которого вы реализовали специализацию.
Одним из недостатков всего этого является то, что неподдерживаемые посещения обнаруживаются только во время выполнения.
Для общей реализации Вистор я бы предложил Локи посетитель, часть Библиотека Локи.
Какая жалость, что эта статья, кажется, исчезла: она вызвала у меня интерес. Полагаю, восемь лет в Интернете - это вечность.