Код класса вызывает функцию в потомке. (Понимаю, что странно, но есть такие костыли?)

Модератор: Модераторы разделов

Аватара пользователя
Zeus
Сообщения: 694

Код класса вызывает функцию в потомке.

Сообщение Zeus »

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

2. Функций будет несколько: сколько и как они будут называться - неизвестно.
Известны только параметры и возвращаемые значения (у всех одинаковые).

3. Это должно быть максимально прозрачно для программиста дочернего класса.
В идеале: он вызывает некоторую служебную функцию базового класса, передавая указатели на свои функции и дальше сидит, ждёт вызовов.

Пока есть мысли только про статические функции (передавать базовому классу указатели на них), но потом нужно вводить код в контекст объекта - т.е. делать парную не статическую функцию: это не сильно прозрачно для программиста дочернего класса.
Спасибо сказали:
Аватара пользователя
serzh-z
Бывший модератор
Сообщения: 8259
Статус: Маньяк
ОС: Arch, Fedora, Ubuntu

Re: Код класса вызывает функцию в потомке.

Сообщение serzh-z »

См. виртуальные функции.
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

Если бы всё было так просто...

сколько и как они будут называться - неизвестно
Спасибо сказали:
sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
17.10.2007 13:30

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) Сомневаюсь, что меня удастся убедить, что для этой задачи нам нужно наследование. :happy:
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:
Аватара пользователя
drBatty
Сообщения: 8735
Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
ОС: Slackware-current

Re: Код класса вызывает функцию в потомке.

Сообщение drBatty »

Я не очень понял, зачем это нужно? Почему нельзя ограничится одной виртуальной функцией, которая будет выполнять все задачи в зависимости от дополнительного целого параметра? Насчёт имён вообще не понял, а переменное количество виртуальных функций может и можно сделать(я незнаю как, и зачем), но действительно - нужно ли тут наследование?
http://emulek.blogspot.ru/ Windows Must Die
Учебник по sed зеркало в github

Скоро придёт
Осень
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

1. Наследование нужно.
Базовый класс наделяет своих потомков определённой функциональностью, которая в частности реализуется через вызовы их, потомков, неведомые функции.

2. Проблема с указателями как-раз в том, что в базовом классе ничего неизвестно о потомках.

3. По поводу одной виртуальной функции, работающей с параметрами - как-раз в этом направлении и приходится двигаться.
Спасибо сказали:
sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
17.10.2007 15:30
1. Наследование нужно.
Базовый класс наделяет своих потомков определённой функциональностью, которая в частности реализуется через вызовы их, потомков, неведомые функции.

1. Есть риск, что тут стандартный промах "наследование реализации" вместо наследования интерфейса.
2. Если функции неведомые - как их базовый класс может вызывать (логически, не про компилятор)? Значит, произодный класс пихает базовому какую-то криптоструктуру, которую тот должен переварить --->> близко к производству кустарных интерпретаторов. :happy:
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) есть близкое к первому варианту прямолинейное решение для кода в первом моем комме; плюс по идее еще есть один хак с тем кодом, что в том комме приводил, но я ему учить не буду :happy:

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

См. первое.
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

Склоняюсь к такому:

Код: Выделить всё

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: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
17.10.2007 19:37
Склоняюсь к такому:
const boost::any& value


Гм, ну по логике вещей - это шаг назад. 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
    ...
  }


Но это все так, в общем виде. Поскольку задача изложена крайне туманно, то автору всегда виднее. :happy:
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

Ладно, опишу подробнее задачу.

Проектируется система, которая будет содержать объекты со свойствами.
Объекты умеют устанавливать и отдавать значение свойств и попутно - рассылать новое значение свойства заинтересованным в этом объектам.
Что-то вроде "псевдокода":

Код: Выделить всё

// Класс, управляющий свойствами объекта.
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: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
17.10.2007 23:21
Ладно, опишу подробнее задачу.

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

О, это очень отличается от того, что мерещилось по ранним описаниям проблемы. :happy:
Собственно, стандартная задачка под ключевые слова "пропертизы", "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
Спасибо сказали:
v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Код класса вызывает функцию в потомке.

Сообщение v04bvs »

Zeus писал(а):
17.10.2007 23:21
Ладно, опишу подробнее задачу.

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

В С++ нет адекватной поддержки компонентной модели. Если есть возможность - лучше перепроектировать на ОО модель, либо сменить язык.

Идеальным вариантом было бы, если бы программист дочернего класса просто вызывал бы какую-то функцию базового класса PropertyAcceptor, передавал бы ей указатель на самолично сочинённую функцию в своём классе

А в чём вопрос? В С++ есть указатель на функции-члены класса.
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

v04bvs писал(а):
18.10.2007 21:39
Идеальным вариантом было бы, если бы программист дочернего класса просто вызывал бы какую-то функцию базового класса PropertyAcceptor, передавал бы ей указатель на самолично сочинённую функцию в своём классе

А в чём вопрос? В С++ есть указатель на функции-члены класса.

Этот указатель выглядит примерно так: MyClass::*MyFunction
И базовый класс должен его запомнить, чтобы при случае вызывать.

А он, базовый класс, ни о MyClass (своём наследнике), ни о MyFunction ничего не знает.
Спасибо сказали:
sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
19.10.2007 00:17
Этот указатель выглядит примерно так: MyClass::*MyFunction
И базовый класс должен его запомнить, чтобы при случае вызывать.

А он, базовый класс, ни о MyClass (своём наследнике), ни о MyFunction ничего не знает.


Ну так кто мешает ее завернуть в потомка AbstractCaller, передавать и сохранять (смарт-) указатель на него в базовом классе?
Код класса вызывает функцию в потомке.
Да и привязка к базовому классу тут несколько искусственна - в любом классе можно сохранять... (тогда и ссылку на объект надо пихать в Коллера, не только указатель на метод)
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Спасибо сказали:
Alexey-S
Сообщения: 46
ОС: WinXP Mandriva

Re: Код класса вызывает функцию в потомке.

Сообщение Alexey-S »

Возьмем для примера модель, используемую в 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: Код класса вызывает функцию в потомке.

Сообщение Zeus »

sergio
Посмотрю в сторону функторов.

Alexey-S
Честно говоря не понял предлагаемую архитектуру.
Спасибо сказали:
Alexey-S
Сообщения: 46
ОС: WinXP Mandriva

Re: Код класса вызывает функцию в потомке.

Сообщение Alexey-S »

Zeus писал(а):
19.10.2007 10:25
Alexey-S
Честно говоря не понял предлагаемую архитектуру.

Мы создаем систему оповещения о смене свойств нашего компонента.
Знаем, что есть некоторые компоненты, которые заинтересованы в получении этой информации (listener).
Нам остаётся всего лишь зарегистрировать в исходном компоненте слушателей.

Всё это делается на уровне базового класса - регистрация слушателей и рассылка сообщения.
Разрабатывая метод изменения некоторого параметра в дочернем объекте, мы вызываем метод
firePropertyChange базового объекта.

Вашу часть задачи мы реализовали - дочерний объект в своём методе вызывает только один метод базового класса.
Остаётся придумать один или несколько объектов-слушателей.

Как же это работает в графическом интерфейсе?

1) Есть визуальный компонент умеющий редактировать строку.
2) Мы хотим, чтобы в зависимости от введенного текста другой графический объект был активным или пассивным.

По условиям задачи мы создаём объект-слушатель. Регистрируем его в первом объекте - строке редактирования.
Передаем этому слушателю объект, активностью которого он должен управлять.

Теперь, когда мы говорим строка_редактора.setText("бла-бла-бла"), слушатель получает сообщение и принимает решение активизировать второй компонент или нет.
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

Что-то всё-равно мало что понятно.
Задача стоит не в создании "оповещателя об изменении свойств" - это уже написано и этим занимается базовый класс PropertyManager.
Сложность - в получении сообщения об изменении свойства классом PropertyAcceptor'ом. Точнее, какой-то неизвестной пока функцией его неизвестного пока потомка.
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

sergio писал(а):
18.10.2007 21:12
Собственно, стандартная задачка под ключевые слова "пропертизы", "ValueModel", "eventListener", и др. ))

Яндекс на строку поиска: "C++ ValueModel" третьим номером выдаёт ссылку на эту тему :laugh:
Спасибо сказали:
sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
20.10.2007 12:31
sergio писал(а):
18.10.2007 21:12
Собственно, стандартная задачка под ключевые слова "пропертизы", "ValueModel", "eventListener", и др. ))

Яндекс на строку поиска: "C++ ValueModel" третьим номером выдаёт ссылку на эту тему :laugh:

Круто.
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
Спасибо сказали:
andy128k
Сообщения: 28
ОС: GNU/Linux

Re: Код класса вызывает функцию в потомке.

Сообщение andy128k »

Ну а как на счёт boost::function? а пользователь (потомок, если хотите) класса передёт базовому замыкание (boost::bind).
Спасибо сказали:
Аватара пользователя
sarutobi
Сообщения: 676
Статус: Добрость и скромнота
ОС: Debian 5, FreeBSD 6.2/8.0

Re: Код класса вызывает функцию в потомке.

Сообщение sarutobi »

возможно, я чего то не понимаю, но это либо попытка написать свой собственный CORBA-подобный инструмент, либо непонимание схемы "издатель-подписчик"........
Fire and water, earth and sky - mistery surrounds us, legends never die!
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

sergio
OK, посмотрю что за зверь - sigc.

andy128k
Я в boost куда не сунусь - везде шаблоны.
А у меня часть кода уже скомпилирована будет - как-то непонятно как шаблоны прикручивать.

sarutobi
Можно и с CORBA сравнить: аттрибуты, подписка на них.
Но там маршалинг-анмаршалинг работает, а ради такого плёвого дела наворачивать CORBA что-то не охота.
Спасибо сказали:
sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
20.10.2007 21:10
sergio
OK, посмотрю что за зверь - sigc.

Посмотреть на досуге наверно стоит (начать можно с быстрого взгляда на хелловорлды 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. Вероятно, близко к тому в дельфи-билдере, нюансов не ведаю. Такой механизм поддерживает перл - но не для регулярного использования, а для спецзадач, перехват обращений к переменным пакета или что-то в этом роде, я не ковырялся.
Какой в этом все великий смысл - сказать трудно, основный видимо в том, чтобы сделать для ламера пользующего "компоненты" жизнь простой и удивительной. :happy: Сократить набор и не создавать методы доступа, пока они не понадобились. В перл, как сказал, это вроде пользуется только для подводного колдовства.
На си плюс подобную муть сделать можно (очень похожую на первый взгляд на таких же тупых примерах :happy: ), но работать она будет не так стройно как мечталось. По тем же причинам, по каким смартпоинтеры не получается использовать совсем как сырые указатели, и так далее...

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
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

Дык я ж говорю:
Есть контейнер свойств.
У него лежит std::map<std::string, boost::any&> properties;
Есть методы чтения/записи свойств.
Это всё скомпилировано в библиотеку и является базовым классом для пользовательских классов.

Некоторым программистам, пишущим эти пользовательские классы, захочется не просто читать-писать свойства других объектов, но и получать "уведомления" об их изменении. Чтобы, при изменении какого-либо свойства, дёргалась функция somethingChanged (const boost::any&) в этом "прикладном классе".
А при изменении другого свойства другого объекта - дёргалась функция anotherOneChanged (const boost::any&) в этом же классе.
И чтобы было это максимально прозрачно для "прикладного программиста" (ламера - как его назвали :laugh: ).
В идеале: он вызывает некоторую функцию, передаёт ей указатель на объект, имя свойства этого объекта и указатель на свою функцию, которую "потроха библиотеки" будут вызывать, когда свойство изменится.
И всё.

Почитав мнения уважаемых собеседников, думаю "грести" в таком направлении:

1. Контейнер свойств "знает" о некоем "адаптере". Адаптер - интерфейс (чистый вабстрактный класс), имеющий функцию accept (const boost::any&) = 0;
Он скомпилирован и лежит в библиотеке, вместе с классом-контейнером.

2. Интерфейс этот реализуется шаблоном, в конструктор которого будет передаваться указатели на объект и функцию-член.
Шаблон, естественно, не скомпилирован, а передаётся в виде *.h'ника "прикладному программисту" (который будет писать реализации объектов).
Этот шаблон - помощь, иначе ему бы пришлось реализовывать интерфейс самому, а функциональность у них, в общем-то, у всех одинаковая - вызвать функцию по указателю и всё. Только типы отличаются. Само просится в шаблон.

3. Программист создаёт из шаблонов объекты-адаптеры, передавая им указатель this и указатель на функцию, а затем у целевого объекта вызывает функцию (она будет в базовом классе, классе контейнера) для установки связи, передавая ей соответствующий адаптер. Возможно смысл функции будет такой, что вызывать её нужно будет в своём объекте, а не в том, свойства которого собираешься "слушать".

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

А в целом - примерно так.
Спасибо сказали:
sergio
Сообщения: 436
Статус: Интересующийся новичок
ОС: Debian GNU/Linux 4 & 5

Re: Код класса вызывает функцию в потомке.

Сообщение sergio »

Zeus писал(а):
21.10.2007 00:52
Дык я ж говорю:
Есть контейнер свойств.
У него лежит std::map<std::string, boost::any&> properties;

Ну так в первоначальной постановке этого не было, оно появилось позже, как версия решения. =)

Есть методы чтения/записи свойств.
Это всё скомпилировано в библиотеку и является базовым классом для пользовательских классов.

Некоторым программистам, пишущим эти пользовательские классы, захочется не просто читать-писать свойства других объектов, но и получать "уведомления" об их изменении. Чтобы, при изменении какого-либо свойства, дёргалась функция somethingChanged (const boost::any&) в этом "прикладном классе".
А при изменении другого свойства другого объекта - дёргалась функция anotherOneChanged (const boost::any&) в этом же классе.
И чтобы было это максимально прозрачно для "прикладного программиста" (ламера - как его назвали :laugh: ).
В идеале: он вызывает некоторую функцию, передаёт ей указатель на объект, имя свойства этого объекта и указатель на свою функцию, которую "потроха библиотеки" будут вызывать, когда свойство изменится.
И всё.


Если исходя из этого.

Базовый класс 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
Спасибо сказали:
Аватара пользователя
Zeus
Сообщения: 694

Re: Код класса вызывает функцию в потомке.

Сообщение Zeus »

Ну я примерно так и описал (правда не ссылками, а указателями), ну и предоставил для программиста производных классов готовый класс-коннектор в виде шаблона.
Спасибо сказали:
Аватара пользователя
sarutobi
Сообщения: 676
Статус: Добрость и скромнота
ОС: Debian 5, FreeBSD 6.2/8.0

Re: Код класса вызывает функцию в потомке.

Сообщение sarutobi »

либо еще один вариант решения.... как в JavaBean сделано:
ввести 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: Код класса вызывает функцию в потомке.

Сообщение Zeus »

"С одной виртуальной функцией" - такое решение рассматривалось.
Там придётся в том или ином виде реализовывать switch, чтобы определять что за свойство изменилось и какую функцию нужно вызывать, для обработки события.
Спасибо сказали:
andy128k
Сообщения: 28
ОС: GNU/Linux

Re: Код класса вызывает функцию в потомке.

Сообщение andy128k »

Zeus писал(а):
20.10.2007 21:10
Я в boost куда не сунусь - везде шаблоны.
А у меня часть кода уже скомпилирована будет - как-то непонятно как шаблоны прикручивать.


Это вообще не имеет значения. 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 ) );
Спасибо сказали: