Код класса вызывает функцию в потомке. (Понимаю, что странно, но есть такие костыли?)
Модератор: Модераторы разделов
-
Zeus
- Сообщения: 694
Код класса вызывает функцию в потомке.
Допусловия:
1. В базовый класс можно внести какую угодно функциональность, но во время создания дочернего класса, базовый класс будет уже скомпилирован.
2. Функций будет несколько: сколько и как они будут называться - неизвестно.
Известны только параметры и возвращаемые значения (у всех одинаковые).
3. Это должно быть максимально прозрачно для программиста дочернего класса.
В идеале: он вызывает некоторую служебную функцию базового класса, передавая указатели на свои функции и дальше сидит, ждёт вызовов.
Пока есть мысли только про статические функции (передавать базовому классу указатели на них), но потом нужно вводить код в контекст объекта - т.е. делать парную не статическую функцию: это не сильно прозрачно для программиста дочернего класса.
1. В базовый класс можно внести какую угодно функциональность, но во время создания дочернего класса, базовый класс будет уже скомпилирован.
2. Функций будет несколько: сколько и как они будут называться - неизвестно.
Известны только параметры и возвращаемые значения (у всех одинаковые).
3. Это должно быть максимально прозрачно для программиста дочернего класса.
В идеале: он вызывает некоторую служебную функцию базового класса, передавая указатели на свои функции и дальше сидит, ждёт вызовов.
Пока есть мысли только про статические функции (передавать базовому классу указатели на них), но потом нужно вводить код в контекст объекта - т.е. делать парную не статическую функцию: это не сильно прозрачно для программиста дочернего класса.
-
serzh-z
- Бывший модератор
- Сообщения: 8259
- Статус: Маньяк
- ОС: Arch, Fedora, Ubuntu
Re: Код класса вызывает функцию в потомке.
См. виртуальные функции.
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
Если бы всё было так просто...
сколько и как они будут называться - неизвестно
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
1). Нужно разрисовывать, не слишком понятно.
Пока мерещится примерно так:
Код: Выделить всё
class Base {
int performTask(int (DerivedClass::* m_ptr)(void)) {
.....
return (this->* m_ptr)();
}
};
class Derived : Base {
someMethod() {
this->performTask(& Derived::taskA);
this->performTask(& Derived::taskB);
this->performTask(& Derived::taskC);
}
int taskA(void);
int taskB(void);
int taskC(void);
};Если имелось в виду что-то подобное - то проблема как объявить указатель на член неведомого производного класса в базовом (никак). У этой проблемы есть простое решение, но... 2)
2) Сомневаюсь, что меня удастся убедить, что для этой задачи нам нужно наследование.
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Код класса вызывает функцию в потомке.
Я не очень понял, зачем это нужно? Почему нельзя ограничится одной виртуальной функцией, которая будет выполнять все задачи в зависимости от дополнительного целого параметра? Насчёт имён вообще не понял, а переменное количество виртуальных функций может и можно сделать(я незнаю как, и зачем), но действительно - нужно ли тут наследование?
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
1. Наследование нужно.
Базовый класс наделяет своих потомков определённой функциональностью, которая в частности реализуется через вызовы их, потомков, неведомые функции.
2. Проблема с указателями как-раз в том, что в базовом классе ничего неизвестно о потомках.
3. По поводу одной виртуальной функции, работающей с параметрами - как-раз в этом направлении и приходится двигаться.
Базовый класс наделяет своих потомков определённой функциональностью, которая в частности реализуется через вызовы их, потомков, неведомые функции.
2. Проблема с указателями как-раз в том, что в базовом классе ничего неизвестно о потомках.
3. По поводу одной виртуальной функции, работающей с параметрами - как-раз в этом направлении и приходится двигаться.
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
1. Есть риск, что тут стандартный промах "наследование реализации" вместо наследования интерфейса.
2. Если функции неведомые - как их базовый класс может вызывать (логически, не про компилятор)? Значит, произодный класс пихает базовому какую-то криптоструктуру, которую тот должен переварить --->> близко к производству кустарных интерпретаторов.
3. Если есть
-переменный набор функций
то
- есть переменный процессор, с общим вызовом (или десятью вызовами, но общими) на входе, передающий его переменному набору функций в нужной последовательности.
Откуда имеем:
Код: Выделить всё
class AbstractTask {
virtual T operator ()(void) = 0;
};
class Derived1::Task : public AbstractTask {
Task(Derived& obj) : _obj(obj) { }
virtual T operator ()(void) {
_obj.taskA();
_obj.taskB();
_obj.taskC();
return;
}
Derived& _obj;
};
class Base {
T performTask(AbstractTask& t) {
return t();
}
};
class Derived1 {
voi someMethod () {
Task t(* this);
this->performTask(t);
}
};2. Проблема с указателями как-раз в том, что в базовом классе ничего неизвестно о потомках.
1) См. предыдущее.
2) Сами предложили вариант:
Код: Выделить всё
static int Derived1::taskA(Base& obj);
Derived::someMethod() {
this->performTask(& Derived1::taskA);
}
Base::performTask(int (* f_ptr)(Base& obj) {
f_ptr(* this);
}3. По поводу одной виртуальной функции, работающей с параметрами - как-раз в этом направлении и приходится двигаться.
См. первое.
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
Склоняюсь к такому:
Код: Выделить всё
class PropertyAcceptor
{
public:
virtual void accept (unsinged long linkKey, const boost::any& value)=0;
};
class Obj: public PropertyAcceptor
{
private:
enum links
{
Name = 1, Length, Speed
};
void NameChanged (const boost::any& value);
void LengthChanged (const boost::any& value);
void SpeedChanged (const boost::any& value);
public:
virtual void accept (unsigned long linkKey, const boost::any& value);
};
Obj::accept (unsigned long linkKey, const boost::any& value)
{
switch (linkKey)
{
case Name: NameChanged (value);
case Length: NameChanged (value);
case Speed: NameChanged (value);
};
};-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
Гм, ну по логике вещей - это шаг назад. Variant-ы м.б. нужны при связывании чего-то с чем-то, стыковке с интерпретаторами и проч. В своем собственном коде это как бы странно - виртуальные вызовы делались для того, чтобы этих свитчей(структ.поле_типа) как раз не было.
В принципе, изначальная постановка - отдать в базовый класс "имена методов" производного, чтобы тот их вызвал - решается просто и тупо через дополнительный индирекшн - отдачу условно говоря функционального объекта с сохраненным значением указателя на метод.
Код: Выделить всё
class MtdCaller
: public AbstractCaller
{
typedef void (Derived::* m_MtdType)(void);
public:
MtdCaller(m_MtdType m_ptr) : _m_ptr(m_ptr) { }
virtual void operator ()(Base& obj) {
try { // DEBUG
Derived& o = dynamic_cast<Derived&>(obj);
}
catch bad_cast ............. // DEBUG
(obj.* _m_ptr)();
return;
}
m_MtdType _m_ptr;
};правим в шаблон и пользуем со всеми Derived. Это если на изначальный вопрос отвечать буквально.
Но вообще задачу надо мыслить ширше, ксож она совсем не расписана была. Я тут пока по делам ездил, врубился, что в изначальной постановке вы там алгоритм шиворот-навыворот вывернули. Не в базовом классе метод делает работу, вызывая между делом неведомые функции из произодного, о которых он не знает и знать не может, а в методе производного определяется вся стратегия работы, и он в нужном порядке вызывает функции-строительные блоки из базового (или свои собственные).
В базовом классе тот рабочий код, который был "общий" между вызовами методов производных классов - оформляется в пачку отдельных методов,
Код: Выделить всё
protected:
virtual void checkPreconditions(void);
virtual void preprocess(void);
virtual void prepare(void);
virtual void process(void);
virtual void finalize(void);
virtual void postprocess(void);
virtual void checkPostconditions(void);в производном - перекрывается метод, определяющий стратегию (и опционально рабочие процедуры базового)
Код: Выделить всё
public:
virtual void performWork(void) {
...
this->checkPreconditions();
...
do smth else
...
this->preprocess();
...
do smth else
...
}Но это все так, в общем виде. Поскольку задача изложена крайне туманно, то автору всегда виднее.
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
Ладно, опишу подробнее задачу.
Проектируется система, которая будет содержать объекты со свойствами.
Объекты умеют устанавливать и отдавать значение свойств и попутно - рассылать новое значение свойства заинтересованным в этом объектам.
Что-то вроде "псевдокода":
Вот это всё компилится и с набором *.h'ников отдаётся "прикладному" программисту.
Он создаёт свои классы, наследует их от Object'а - это будут обычные объекты со свойствами.
А если ему нужно получать уведомления об изменении свойств других объектов - класс нужно отнаследовать от PropertyAcceptor (потому что только с ним умеет работать PropertyManager). Ну и нужно наполнить его содержанием, как я уже выше написал.
Идеальным вариантом было бы, если бы программист дочернего класса просто вызывал бы какую-то функцию базового класса PropertyAcceptor, передавал бы ей указатель на самолично сочинённую функцию в своём классе, указатель на объект PropertyManager и имя свойства в нём - и всё.
Остальная работа происходила бы в недрах PropertyAcceptor'а и PropertyManager'а - а программёр просто получал бы вызовы своих функций при изменении требуемого свойства.
Проектируется система, которая будет содержать объекты со свойствами.
Объекты умеют устанавливать и отдавать значение свойств и попутно - рассылать новое значение свойства заинтересованным в этом объектам.
Что-то вроде "псевдокода":
Код: Выделить всё
// Класс, управляющий свойствами объекта.
class PropertyManager
{
setValue (const string& name, const boost::any& value)
{
propserties[name] = value;
/*
У всех объектов заинтересованных в получении нового значения этого свойства вызывается метод accept (см.ниже).
*/
for (begin; end; (PropertyAcceptorIter++)->accept(value));
};
const boost::any& getValue ();
link (string thisObjPropertyName, PropertyAcceptor* thoseObjPtr, ulong linkKey);
};
// Интерфейс (чисто абстрактный класс) объекта, желающего получать изменения свойств.
class PropertyAcceptor
{
virtual void accept (ulong linkKey, const boost::any& value)=0;
};
// Это базовый класс всех объектов проекта - у всех них есть свойства.
class Object: public PropertyManager
{
};Вот это всё компилится и с набором *.h'ников отдаётся "прикладному" программисту.
Он создаёт свои классы, наследует их от Object'а - это будут обычные объекты со свойствами.
А если ему нужно получать уведомления об изменении свойств других объектов - класс нужно отнаследовать от PropertyAcceptor (потому что только с ним умеет работать PropertyManager). Ну и нужно наполнить его содержанием, как я уже выше написал.
Идеальным вариантом было бы, если бы программист дочернего класса просто вызывал бы какую-то функцию базового класса PropertyAcceptor, передавал бы ей указатель на самолично сочинённую функцию в своём классе, указатель на объект PropertyManager и имя свойства в нём - и всё.
Остальная работа происходила бы в недрах PropertyAcceptor'а и PropertyManager'а - а программёр просто получал бы вызовы своих функций при изменении требуемого свойства.
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
О, это очень отличается от того, что мерещилось по ранним описаниям проблемы.
Собственно, стандартная задачка под ключевые слова "пропертизы", "ValueModel", "eventListener", и др. )) Т.е. близкие решения могут быть рассмотрены в гуях смоллток и ява, куте, гткмм, хфс, "пропертизах" дельфи-билдер и многих других. ))
Идеальным вариантом было бы, если бы программист дочернего класса просто вызывал бы какую-то функцию базового класса PropertyAcceptor, передавал бы ей указатель на самолично сочинённую функцию в своём классе, указатель на объект PropertyManager и имя свойства в нём - и всё.
Остальная работа происходила бы в недрах PropertyAcceptor'а и PropertyManager'а - а программёр просто получал бы вызовы своих функций при изменении требуемого свойства.
Как вы понимаете, нет ничего невозможного или особо сложного в том, чтобы реализовать эти идеи на чистых плюсах. Проблема в том, что не представляя специфики использования и типовых примеров, никто не сможет гарантировать, что это будет удобнее для последующей работы, чем ваш слегка интерпретируемый вариант с текстовыми именами пропертей, Вариантами, и поиском по мэпу. Особенно если система соседствует с действительно текстовым форматом (ХМЛ), особенно если архитектура пока неопределена или может использоваться непредсказуемо (что и мешает определить четкие интерфейсы?) и т.д... Тоже вариант... Если интересно, как это еще можно сделать - вон, пример в gtkmm / XFC c libsigc...
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
v04bvs
- Сообщения: 636
- ОС: Debian GNU/Linux
Re: Код класса вызывает функцию в потомке.
В С++ нет адекватной поддержки компонентной модели. Если есть возможность - лучше перепроектировать на ОО модель, либо сменить язык.
Идеальным вариантом было бы, если бы программист дочернего класса просто вызывал бы какую-то функцию базового класса PropertyAcceptor, передавал бы ей указатель на самолично сочинённую функцию в своём классе
А в чём вопрос? В С++ есть указатель на функции-члены класса.
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
v04bvs писал(а): ↑18.10.2007 21:39Идеальным вариантом было бы, если бы программист дочернего класса просто вызывал бы какую-то функцию базового класса PropertyAcceptor, передавал бы ей указатель на самолично сочинённую функцию в своём классе
А в чём вопрос? В С++ есть указатель на функции-члены класса.
Этот указатель выглядит примерно так: MyClass::*MyFunction
И базовый класс должен его запомнить, чтобы при случае вызывать.
А он, базовый класс, ни о MyClass (своём наследнике), ни о MyFunction ничего не знает.
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
Ну так кто мешает ее завернуть в потомка AbstractCaller, передавать и сохранять (смарт-) указатель на него в базовом классе?
Код класса вызывает функцию в потомке.
Да и привязка к базовому классу тут несколько искусственна - в любом классе можно сохранять... (тогда и ссылку на объект надо пихать в Коллера, не только указатель на метод)
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
Alexey-S
- Сообщения: 46
- ОС: WinXP Mandriva
Re: Код класса вызывает функцию в потомке.
Возьмем для примера модель, используемую в Java Swing.
Можно придумать еще несколько видов моделей...
Код:
// в базовом классе
List listPropertyChangeListener;
firePropertyChange(char* propertyName, void* oldValue, void* newValue) {
for(int i = 0; i < listPropertyChangeListener.size(); i++) {
PropertyChangeListener* listener = listPropertyChangeListener.get(i);
listener->changePropertyValue(propertyName, oldValue, newValue);
}
}
// в дочернем классе
setMyProperty(PropetyValue* value) {
PropetyValue* oldValue = this->value;
this->value = value;
fireProperyChange("myProperty", oldValue, value);
}
Можно придумать еще несколько видов моделей...
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
sergio
Посмотрю в сторону функторов.
Alexey-S
Честно говоря не понял предлагаемую архитектуру.
Посмотрю в сторону функторов.
Alexey-S
Честно говоря не понял предлагаемую архитектуру.
-
Alexey-S
- Сообщения: 46
- ОС: WinXP Mandriva
Re: Код класса вызывает функцию в потомке.
Мы создаем систему оповещения о смене свойств нашего компонента.
Знаем, что есть некоторые компоненты, которые заинтересованы в получении этой информации (listener).
Нам остаётся всего лишь зарегистрировать в исходном компоненте слушателей.
Всё это делается на уровне базового класса - регистрация слушателей и рассылка сообщения.
Разрабатывая метод изменения некоторого параметра в дочернем объекте, мы вызываем метод
firePropertyChange базового объекта.
Вашу часть задачи мы реализовали - дочерний объект в своём методе вызывает только один метод базового класса.
Остаётся придумать один или несколько объектов-слушателей.
Как же это работает в графическом интерфейсе?
1) Есть визуальный компонент умеющий редактировать строку.
2) Мы хотим, чтобы в зависимости от введенного текста другой графический объект был активным или пассивным.
По условиям задачи мы создаём объект-слушатель. Регистрируем его в первом объекте - строке редактирования.
Передаем этому слушателю объект, активностью которого он должен управлять.
Теперь, когда мы говорим строка_редактора.setText("бла-бла-бла"), слушатель получает сообщение и принимает решение активизировать второй компонент или нет.
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
Что-то всё-равно мало что понятно.
Задача стоит не в создании "оповещателя об изменении свойств" - это уже написано и этим занимается базовый класс PropertyManager.
Сложность - в получении сообщения об изменении свойства классом PropertyAcceptor'ом. Точнее, какой-то неизвестной пока функцией его неизвестного пока потомка.
Задача стоит не в создании "оповещателя об изменении свойств" - это уже написано и этим занимается базовый класс PropertyManager.
Сложность - в получении сообщения об изменении свойства классом PropertyAcceptor'ом. Точнее, какой-то неизвестной пока функцией его неизвестного пока потомка.
-
Zeus
- Сообщения: 694
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
Круто.
ValueModel это смоллток. Поэтому...
EventListener - ява.
"Пропертизы" - дельфи, есессно.
А в Си++ это не больно-то прижилось. Вон, в куте/мок там сигналы и слоты, в gtkmm с XFC - сигналы и коннекшны. =) Кто в лес, кто по дрова. )) Вам два последних смотреть, в принципе, но там неприятность того рода, что они обертки к сишному ГТК, поэтому... чтение на любителя, боюсь. Но система сигналов в обоих работает на lib sigc, на сайтах GTK и XFC по ней документация вроде была какая-то... так что если вообще что-то хочется смотреть, то наверное именно sigc.
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
andy128k
- Сообщения: 28
- ОС: GNU/Linux
Re: Код класса вызывает функцию в потомке.
Ну а как на счёт boost::function? а пользователь (потомок, если хотите) класса передёт базовому замыкание (boost::bind).
-
sarutobi
- Сообщения: 676
- Статус: Добрость и скромнота
- ОС: Debian 5, FreeBSD 6.2/8.0
Re: Код класса вызывает функцию в потомке.
возможно, я чего то не понимаю, но это либо попытка написать свой собственный CORBA-подобный инструмент, либо непонимание схемы "издатель-подписчик"........
Fire and water, earth and sky - mistery surrounds us, legends never die!
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
sergio
OK, посмотрю что за зверь - sigc.
andy128k
Я в boost куда не сунусь - везде шаблоны.
А у меня часть кода уже скомпилирована будет - как-то непонятно как шаблоны прикручивать.
sarutobi
Можно и с CORBA сравнить: аттрибуты, подписка на них.
Но там маршалинг-анмаршалинг работает, а ради такого плёвого дела наворачивать CORBA что-то не охота.
OK, посмотрю что за зверь - sigc.
andy128k
Я в boost куда не сунусь - везде шаблоны.
А у меня часть кода уже скомпилирована будет - как-то непонятно как шаблоны прикручивать.
sarutobi
Можно и с CORBA сравнить: аттрибуты, подписка на них.
Но там маршалинг-анмаршалинг работает, а ради такого плёвого дела наворачивать CORBA что-то не охота.
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
Посмотреть на досуге наверно стоит (начать можно с быстрого взгляда на хелловорлды gtkmm или XFC чтобы увидеть как оно смотрится снаружи). Проблема в том, я боюсь, что мы тут Zeus-у морочим голову усиленно, поскольку никому кроме автора вопроса не известно и пока не понятно, что именно он делает, в каком контексте оно исполозуется и что там надо-ненадо.
ValueModel - модель как модель, в плюсах будет выглядеть как ValueModel<T>, методы get/setValue, add/remove Listener/Observer, опционально что-нибудь вроде get/set RawData (в обход генерилки событий) и freeze/reviveEvents чтобы их временно заблокировать. Соотв-но объект ValueModel либо содержит свой список ссылок на Listener-ы, либо подключается при создании к какому-то более глобальному - детали реализации, но логически список у него в любом случае персональный.
"проперти" в классическом виде: член width класса MyClass поддерживает операции вида:
MyClass mc ;
mc.width = 200 ;
int w = mc.width ;
Изначально запись-чтение width осуществляется напрямую, как из структруры. Разработчик класса в любой момент (в т.ч. уже используемый в работе класс) может определить методы контроля доступа скажем __access/__modifyWidth или только один из, и при следующих вызовах
mc.width = 200 ;
int w = mc.width ;
они, соотв-но, будут скрыто и невидимо вызваны для получения-записи значения.
Так это вроде в VB. Вероятно, близко к тому в дельфи-билдере, нюансов не ведаю. Такой механизм поддерживает перл - но не для регулярного использования, а для спецзадач, перехват обращений к переменным пакета или что-то в этом роде, я не ковырялся.
Какой в этом все великий смысл - сказать трудно, основный видимо в том, чтобы сделать для ламера пользующего "компоненты" жизнь простой и удивительной.
На си плюс подобную муть сделать можно (очень похожую на первый взгляд на таких же тупых примерах
Event, EventListener, signal, slot, прочее. "События" привязываются не к значению отдельной переменной, а к логическому состоянию объекта в целом. Моделью является сложный аггрегатный объект, у него может быть много разных "событий". Внутри объекта вся механика настраивается вручную, методы setWidth setHeight содержат проверку типа
Код: Выделить всё
if (newvalue != oldvalue)
this->value = newvalue
this->fireEvent(new GeometryChangedEvent(this, WIDTH, oldvalue, newvalue))объект хранит список зарегистрированных через add/removeListener "слушателей" (для каждого события скорее всего свой отдельный список) и рассылает им уведомления. Плюс те же freeze/revive и желательность аггрегатных методов типа setGeometry(width, height) для оптимизации.
PropertyMap - таблица, хранящая простые величины, причем скорее всего полученные из конфига или сис-окружения, скорее всего в текстовом виде, где производится поиск нужного значения вызовом типа getProperty(имя), использующим кодом оно проверяется, конвертится и пользуется. Если может модифицироваться - то вероятно может понадобиться механизм с Listener-ами и тут, и логично, что он будет рассылать одно сообщение PropertyMapChanged с указанием имени переменной, которая поменялась, ссылками на значения и остальное как обычно...
Схемы-то совершенно разные, данные разные, все разное. Чего надо-то?
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
Дык я ж говорю:
Есть контейнер свойств.
У него лежит std::map<std::string, boost::any&> properties;
Есть методы чтения/записи свойств.
Это всё скомпилировано в библиотеку и является базовым классом для пользовательских классов.
Некоторым программистам, пишущим эти пользовательские классы, захочется не просто читать-писать свойства других объектов, но и получать "уведомления" об их изменении. Чтобы, при изменении какого-либо свойства, дёргалась функция somethingChanged (const boost::any&) в этом "прикладном классе".
А при изменении другого свойства другого объекта - дёргалась функция anotherOneChanged (const boost::any&) в этом же классе.
И чтобы было это максимально прозрачно для "прикладного программиста" (ламера - как его назвали
).
В идеале: он вызывает некоторую функцию, передаёт ей указатель на объект, имя свойства этого объекта и указатель на свою функцию, которую "потроха библиотеки" будут вызывать, когда свойство изменится.
И всё.
Почитав мнения уважаемых собеседников, думаю "грести" в таком направлении:
1. Контейнер свойств "знает" о некоем "адаптере". Адаптер - интерфейс (чистый вабстрактный класс), имеющий функцию accept (const boost::any&) = 0;
Он скомпилирован и лежит в библиотеке, вместе с классом-контейнером.
2. Интерфейс этот реализуется шаблоном, в конструктор которого будет передаваться указатели на объект и функцию-член.
Шаблон, естественно, не скомпилирован, а передаётся в виде *.h'ника "прикладному программисту" (который будет писать реализации объектов).
Этот шаблон - помощь, иначе ему бы пришлось реализовывать интерфейс самому, а функциональность у них, в общем-то, у всех одинаковая - вызвать функцию по указателю и всё. Только типы отличаются. Само просится в шаблон.
3. Программист создаёт из шаблонов объекты-адаптеры, передавая им указатель this и указатель на функцию, а затем у целевого объекта вызывает функцию (она будет в базовом классе, классе контейнера) для установки связи, передавая ей соответствующий адаптер. Возможно смысл функции будет такой, что вызывать её нужно будет в своём объекте, а не в том, свойства которого собираешься "слушать".
Ну, там ещё в глубине всякая фоновая работа ведётся: связь устанавливается двойная, адаптер и контейнер обмениваются указателями друг на друга и в деструкторах - сообщают друг другу о разрыве связи, ещё защиты данных при многопоточной работе - но это уже всякие технические мелочи.
А в целом - примерно так.
Есть контейнер свойств.
У него лежит std::map<std::string, boost::any&> properties;
Есть методы чтения/записи свойств.
Это всё скомпилировано в библиотеку и является базовым классом для пользовательских классов.
Некоторым программистам, пишущим эти пользовательские классы, захочется не просто читать-писать свойства других объектов, но и получать "уведомления" об их изменении. Чтобы, при изменении какого-либо свойства, дёргалась функция somethingChanged (const boost::any&) в этом "прикладном классе".
А при изменении другого свойства другого объекта - дёргалась функция anotherOneChanged (const boost::any&) в этом же классе.
И чтобы было это максимально прозрачно для "прикладного программиста" (ламера - как его назвали
В идеале: он вызывает некоторую функцию, передаёт ей указатель на объект, имя свойства этого объекта и указатель на свою функцию, которую "потроха библиотеки" будут вызывать, когда свойство изменится.
И всё.
Почитав мнения уважаемых собеседников, думаю "грести" в таком направлении:
1. Контейнер свойств "знает" о некоем "адаптере". Адаптер - интерфейс (чистый вабстрактный класс), имеющий функцию accept (const boost::any&) = 0;
Он скомпилирован и лежит в библиотеке, вместе с классом-контейнером.
2. Интерфейс этот реализуется шаблоном, в конструктор которого будет передаваться указатели на объект и функцию-член.
Шаблон, естественно, не скомпилирован, а передаётся в виде *.h'ника "прикладному программисту" (который будет писать реализации объектов).
Этот шаблон - помощь, иначе ему бы пришлось реализовывать интерфейс самому, а функциональность у них, в общем-то, у всех одинаковая - вызвать функцию по указателю и всё. Только типы отличаются. Само просится в шаблон.
3. Программист создаёт из шаблонов объекты-адаптеры, передавая им указатель this и указатель на функцию, а затем у целевого объекта вызывает функцию (она будет в базовом классе, классе контейнера) для установки связи, передавая ей соответствующий адаптер. Возможно смысл функции будет такой, что вызывать её нужно будет в своём объекте, а не в том, свойства которого собираешься "слушать".
Ну, там ещё в глубине всякая фоновая работа ведётся: связь устанавливается двойная, адаптер и контейнер обмениваются указателями друг на друга и в деструкторах - сообщают друг другу о разрыве связи, ещё защиты данных при многопоточной работе - но это уже всякие технические мелочи.
А в целом - примерно так.
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: Код класса вызывает функцию в потомке.
Ну так в первоначальной постановке этого не было, оно появилось позже, как версия решения. =)
Есть методы чтения/записи свойств.
Это всё скомпилировано в библиотеку и является базовым классом для пользовательских классов.
Некоторым программистам, пишущим эти пользовательские классы, захочется не просто читать-писать свойства других объектов, но и получать "уведомления" об их изменении. Чтобы, при изменении какого-либо свойства, дёргалась функция somethingChanged (const boost::any&) в этом "прикладном классе".
А при изменении другого свойства другого объекта - дёргалась функция anotherOneChanged (const boost::any&) в этом же классе.
И чтобы было это максимально прозрачно для "прикладного программиста" (ламера - как его назвали).
В идеале: он вызывает некоторую функцию, передаёт ей указатель на объект, имя свойства этого объекта и указатель на свою функцию, которую "потроха библиотеки" будут вызывать, когда свойство изменится.
И всё.
Если исходя из этого.
Базовый класс Base.
Производные (пишутся ламером) DerivedA, DerivedB, DerivedC.
Базовый класс имеет метод для добавления "слушателя" (псевдокод)
Код: Выделить всё
addListener(const std::string& "property_name", Acceptor* a)Метод-акцептор имеет прототип:
Код: Выделить всё
void (Class::*) (const boost::any&);Производные классы регистрируют один из своих методов в базовом классе как должный быть вызываемым при наступлении события.
Проблема: метод в производном классе имеет прототип
Код: Выделить всё
void (DerivedA::*) (const boost::any&);
void (DerivedB::*) (const boost::any&);
void (DerivedC::*) (const boost::any&);1). В базовом классе объявить метод, принимающий указатель на метод производного класса мы не можем.
2). Прототипы методов не совпадают (и приведены к общему виду быть не могут).
Решение: индирекшн через промежуточный объектик. В базовый класс передается объект-адаптер, метод которого вызывается при наступлении события и передает вызов методу производного класса.
Отягчающие обстоятельства: поскольку производных классов у нас больше одного, то прототип метода которому объект-адаптер "передает вызов" - может быть различен.
Решение: объект-адаптер определяет свой вызываемый метод виртуальным. Производные классы объекта-адаптера переопределяют этот метод, производя необходимые преобразования.
Нюансы: для вызова метода по указателю нам необходим объект, против которого указатель разыменовывается. Следовательно, ссылку на этот объект мы либо должны получать при вызове, либо хранить в объекте-адапторе. В последующем коде второй вариант закомментирован.
Код.
Код: Выделить всё
class AbstractConnector {
public:
virtual void operator ()(Base& obj, boost:any& pt) = 0;
// virtual void operator ()(boost:any& pt) = 0; // вариант для хранимой в адаптере ссылки
// virtual void accept(boost:any& pt) = 0; // или так как нравится, пофигу функтор тут без особой
};
// здесь мы понятно хотим шаблон, но для простоты пока пишем для DerivedA
class DerivedAConnector
: public AbstractConnector
{
typedef void (DerivedA::* m_MtdType)(boost:any& pt);
public:
DerivedAConnector(m_MtdType m_ptr) : _m_ptr(m_ptr) { }
// DerivedAConnector(DerivedA& obj, m_MtdType m_ptr) : _obj(obj), _m_ptr(m_ptr) { }
virtual void operator ()(Base& base_obj, boost:any& pt) {
try { // DEBUG
Derived& obj = dynamic_cast<Derived&>(base_obj);
}
catch bad_cast ............. // DEBUG
(obj.* _m_ptr)(pt);
return;
}
/*
virtual void operator ()(boost:any& pt) {
(_obj.* _m_ptr)(pt);
return;
}
*/
// DerivedA& _obj;
m_MtdType _m_ptr;
};В базовом коде у нас
Код: Выделить всё
std::list<AbstractConnector*> listeners;В производном классе DerivedX
Код: Выделить всё
this->addListener("width", new DerivedXConnector(& DerivedX::accptorY));
// this->addListener("width", new DerivedXConnector(* this, & DerivedX::accptorY));Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
Ну я примерно так и описал (правда не ссылками, а указателями), ну и предоставил для программиста производных классов готовый класс-коннектор в виде шаблона.
-
sarutobi
- Сообщения: 676
- Статус: Добрость и скромнота
- ОС: Debian 5, FreeBSD 6.2/8.0
Re: Код класса вызывает функцию в потомке.
либо еще один вариант решения.... как в JavaBean сделано:
ввести PropertyChangeEvent, в котором описывается propertyName, oldValue, newValue;
в классе, который хочет рассылать уведомления - набор propertyChangeListener'ов и функция firePropertyChange(name, oldValue, newValue);
класс который хочет слушать изменения - реализует интерфейс PropertyChangeListener, в котором всего одна виртуальная функция
propertyChange(PropertyChangeEvent pce);
ввести PropertyChangeEvent, в котором описывается propertyName, oldValue, newValue;
в классе, который хочет рассылать уведомления - набор propertyChangeListener'ов и функция firePropertyChange(name, oldValue, newValue);
класс который хочет слушать изменения - реализует интерфейс PropertyChangeListener, в котором всего одна виртуальная функция
propertyChange(PropertyChangeEvent pce);
Fire and water, earth and sky - mistery surrounds us, legends never die!
-
Zeus
- Сообщения: 694
Re: Код класса вызывает функцию в потомке.
"С одной виртуальной функцией" - такое решение рассматривалось.
Там придётся в том или ином виде реализовывать switch, чтобы определять что за свойство изменилось и какую функцию нужно вызывать, для обработки события.
Там придётся в том или ином виде реализовывать switch, чтобы определять что за свойство изменилось и какую функцию нужно вызывать, для обработки события.
-
andy128k
- Сообщения: 28
- ОС: GNU/Linux
Re: Код класса вызывает функцию в потомке.
Это вообще не имеет значения. boost::function -- это просто указатель на функцию.
Код:
typedef boost::function1<void, std::string> Listener;
// в контейнере добавляешь что-то вроде следующего
std::vector<Listener> Listeners;
void addListener(Listener listener) { Listeners.push_back(listener); }
// вызаваешь...
for (...iterator i = Listeners.begin(); ...)
(*i)("property_name");
// клиент регистрирует обработчик
class Client
{
...
void listener(std::stringstr);
...
};
Storage.addListener( boost::bind( &Client::listener, &client_instance, _1 ) );