Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.08.31;
Скачать: CL | DM;

Вниз

CALLBACk процедура как часть класса в С++   Найти похожие ветки 

 
@!!ex ©   (2008-07-10 00:14) [0]

Можно ли?
Суть в том, что надо реализовать подход к событиям аля дельфи.
Когда есть главвный класс, у него куча мемберов, которые должны вызывать CALLBACK функции из главного класса.
Теоретически это точно возможно, а вот как это реализовать практически - не представляю...


 
Simpsons   (2008-07-10 00:21) [1]

А если через события? WM_USER + X?


 
@!!ex ©   (2008-07-10 00:23) [2]

> [1] Simpsons   (10.07.08 00:21)

Классы - не обязательно окна и не обязательно под виндой.


 
guav ©   (2008-07-10 00:28) [3]

Теоретически: есть pointer-to-member-function. Запоминаешь его плюс укзатель на сам класс - вот и событие. Смотри операторы ->* и .* , оттуда найдёшь.
Практически: событие - переменная типа boost::function<R(P)>, обработчик - метод оборачивается в boost::bind, вторым параметром бинда this, далее другие параметры и плейсхолдеры.


 
@!!ex ©   (2008-07-10 00:33) [4]

> [3] guav ©   (10.07.08 00:28)

Теоретическую часть я понял, а вот практическую - нет. У меня нету boost и в MSDN найти не могу.

С указателем на класс проблем нету.
Он у всех мемберов как Parent прописан.
А вот как получить указатель на функцию?


 
guav ©   (2008-07-10 00:45) [5]

> [4] @!!ex ©   (10.07.08 00:33)


> У меня нету boost и в MSDN найти не могу.

А ведь в MSDN уже есть. Это часть будущего стандарта.
http://msdn.microsoft.com/en-us/library/bb982702.aspx
http://msdn.microsoft.com/en-us/library/bb982519.aspx


> А вот как получить указатель на функцию?

struct X
{
 int m1(char)
 {
   puts("hello");
   return 42;
 }
};

int _tmain(int argc, _TCHAR* argv[])
{

 int (X::*pM)(char);
 X *pX;
 X x;
 pX = &x;
 pM = &X::m1; // здесь.
 return (pX->*pM)("z");
}


 
Канадец   (2008-07-10 00:46) [6]

См. Observer design pattern. В частности реализации для C++.


 
@!!ex ©   (2008-07-10 00:54) [7]

> [5] guav ©   (10.07.08 00:45)

Как сделать вот в таком случае:
void* Test = NULL;
void* Class = NULL;

class cTest
{
cTest()
{
 Class = this;
 Test = /*??*/;
};
virtual void Method();
};

{
(cTest)Class->/*??*/;
};

?


 
@!!ex ©   (2008-07-10 00:54) [8]

> А ведь в MSDN уже есть. Это часть будущего стандарта.

У меня 2005, в нем и смотрю...
2008 руки не доходят поставить, а online -  интернет не позволяет.


 
guav ©   (2008-07-10 01:03) [9]

> [8] @!!ex ©   (10.07.08 00:54)

Вообще сильно ли нужны "события как в Делфи" ?

> Когда есть главный класс, у него куча мемберов, которые
> должны вызывать CALLBACK функции из главного класса.

Почему бы не дать всем классам, вызывающим методы главного, ссылку на главный класс ?


> [7] @!!ex ©   (10.07.08 00:54)

никак.
тип pointer-to-member-function не совместим с void*, фактически это не совсем указатель, и его размер может быть больше void* (на MSVC размер у него больше размера void* при наличии виртуального наследования).
тут надо на шаблонах делать, а не на приведениях.


 
wl ©   (2008-07-10 01:34) [10]

ну так может методы сделать статическими и не париться с размерами указателей?


 
wl ©   (2008-07-10 01:51) [11]

а что это для 2008 FP требуется redistributable? там что нельзя статически всё линковать. в бусте кажется это возможно


 
@!!ex ©   (2008-07-10 09:58) [12]

> Вообще сильно ли нужны "события как в Делфи" ?

Ну вот смотрите:
Есть куча классок cLabel, cImage, cTrackBar, cButton и так далее.
Программистом создается куча окон типа форм в Delphi, например сMenuWindow.
События зачем нужны?
Создаем метод OnButtonClick в классе cMenuWindow. Нужно чтобы кнопка вызвала метод OnButtonClick.
Ссылками это разве решабельно?


 
guav ©   (2008-07-10 12:48) [13]


> Ссылками это разве решабельно?

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

В данном случае, я бы делал смотря по ситуации.
события boost::signal
события boost::function
без событий.

Не хочешь буст - смотри другие слот-сигнальные примеры, их хватает.
http://rsdn.ru/article/cpp/delegates.xml
http://rsdn.ru/article/cpp/cxx_events.xml

Может ещё заинтересует применение колбэков как в С, через static метод, который принимает экземпляр класса, и оттуда уже вызывать обычный.


 
@!!ex ©   (2008-07-10 12:56) [14]

> [13] guav ©   (10.07.08 12:48)

Спасибо! Столько нового.... мозги пухнут...
Пока решил делать вот так:
Набор стандартных методов, ObButtonClick, ObImageBarPositionChange и т.д. в базовом классе
В качестве параметра - UID кнопки или другого объекта. В качестве UIDа может выступать указатель.
ТОгда в обработчике:

void cMyWindow::ObButtonClick(int ID)
{
switch(ID)
{
case 0: //первая кнопка
break;
case 1: //вторая кнопка кнопка
break;
//и т.д.
}
}

Насколько это приемлимо?


 
guav ©   (2008-07-10 13:11) [15]

> [14] @!!ex ©   (10.07.08 12:56)
> Насколько это приемлимо?


Настолько, насколько это будет работать. Я ж не дам рекомендаций на все случаи жизни :)
Чтобы не завязываться на конкретный главный класс и не вытворять чудеса на шаблонах, стоит сделать базовый класс с набором методов абстрактным базовым классом, т.е. интерфейсом: все методы pure virtual и пустой виртуальный деструктор, всё это public.

Кстати, Delphi при импорте объекта автоматизации для поддержки диспинтерфейса событий в IDispatch::Invoke ивент-синка генерит код вроде [14].


 
guav ©   (2008-07-10 13:20) [16]

> [11] wl ©   (10.07.08 01:51)
> а что это для 2008 FP требуется redistributable?

Подозреваю что то не redistributable к приложению, а пакет обновления к самой студии или redistributable для других улучшений этого пакета обновлений. А std::tr1::bind и std::tr1::function - header only, как и в бусте, ну нечего там в .cpp писать.


 
GlFox ©   (2008-07-10 13:41) [17]

> [15] guav ©   (10.07.08 13:11)
> Чтобы не завязываться на конкретный главный класс и не вытворять
> чудеса на шаблонах, стоит сделать базовый класс с набором
> методов абстрактным базовым классом, т.е. интерфейсом: все
> методы pure virtual и пустой виртуальный деструктор, всё
> это public.

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


 
wl ©   (2008-07-10 13:54) [18]


> guav ©   (10.07.08 13:20) [16]

нет, есть FP на 330 мег, и есть FP redist на 4 мега.
я уже скачал посмотреть - там библиотеки MFC9.0.
кстати там же есть шаблоны для MFC, чтобы сделать меню в виде риббона, стиль офиса 2007. Выглядит забавно (хотя возможно это и до FP было, не обращал внимания).


 
@!!ex ©   (2008-07-10 14:12) [19]

// CallbackTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>

using namespace std;

class cButton;

class cCallbackInterface
{
      // да, пустой интерфейс =)
      // но виртуальная таблица существует
};

class cMainWnd : public cCallbackInterface
{
      // этот класс наследует интерфейс, но не реализует логику на реакцию дочек.
};

typedef int (cCallbackInterface::*Tpf)(cButton*);

class cButton
{
private:
      cMainWnd*                       m_Parent;
      Tpf                                     m_Action;
public:

      cButton (cMainWnd* pParent, Tpf pf)
              : m_Parent (pParent)
              , m_Action (pf)
      {
      }

      void Click ()
      {
              if (m_Parent)
              {
                      (m_Parent->*m_Action) (this);
              }
      }
};

class cGameMainWnd : public cMainWnd
{
      // а вот этот класс пусть уже реализует логику
public:
      cButton*                        m_NewGameButton;
      cButton*                        m_QuitButton;

      void CreateWnd()
      {
              m_NewGameButton = new cButton (this, (Tpf)&cGameMainWnd::onNewButtonClick);
              m_QuitButton = new cButton (this, (Tpf)&cGameMainWnd::onQuitButtonClick);
      }

      int onNewButtonClick (cButton* caller)
      {
              std::cout << "OnNewGame\n";

              return 0;
      }

      int onQuitButtonClick (cButton* caller)
      {
              std::cout << "OnQuitGame\n";

              return 0;
      }

};

int _tmain(int argc, _TCHAR* argv[])
{
      cGameMainWnd m;

      m.CreateWnd();

      m.m_NewGameButton->Click();
      m.m_QuitButton->Click();

      return 0;
}


 
@!!ex ©   (2008-07-10 14:19) [20]

Код написан человеком, который теоретически мой подчиненный...
Меня учат мои подчиненные... *бьется головой об стенку*


 
guav ©   (2008-07-10 15:05) [21]

Я уже говорил, почему это не легально.
Хотя в MSVC действительно работает, если не использовать виртуальное наследование.


 
@!!ex ©   (2008-07-10 15:55) [22]

> [21] guav ©   (10.07.08 15:05)

Перечитал все внимательно. Думаю опять...
Уже 3 или 4 метод отметается.... :)
Спасибо за активное участие!


 
31512 ©   (2008-07-10 15:59) [23]

Поставь уже Qt и не мучайся.


 
@!!ex ©   (2008-07-10 16:03) [24]

> [23] 31512 ©   (10.07.08 15:59)

Обана, а он умеет работать с OpenGL?


 
wl ©   (2008-07-10 16:26) [25]

умеет. посмотреть пример можно например тут:
Ж. Бланшет, М. Саммерфилд. QT4: Программирование GUI.djvu
страница 234, QGLWidget


 
guav ©   (2008-07-10 16:34) [26]

Пример виртуального наследования:
struct X {};
struct Y {};
struct Z : X, virtual Y {};

int _tmain(int argc, _TCHAR* argv[])
{
 typedef void (X::*P1)(void);
 typedef void (Z::*P2)(void);
 printf("%d  %d\n", sizeof(P1), sizeof(P2));
 return 0;
}


 
wl ©   (2008-07-10 16:37) [27]

а где можно почитать вкратце описание виртуального наследования? хочу понять область применения, никогда раньше не сталкивался, даже в литературе


 
guav ©   (2008-07-10 16:43) [28]

Я синтаксис по MSDN изучал.
http://msdn.microsoft.com/en-us/library/wcz57btd(VS.71).aspx
http://msdn.microsoft.com/en-us/library/9bzywxkx(VS.71).aspx

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


 
31512 ©   (2008-07-11 10:24) [29]


> @!!ex ©   (10.07.08 16:03) [24]

Qt это, разумеется, не VCL. Но похожа (пусть только и внешне) на него очень. Даже релизовали MVC, что является огромным плюсом. Работает не только с OpenGL. С её системой слот/сигнал тебе callback просто нафиг не нужен будет. Ты вообще от системы зависеть не будешь. Есть свои ньюансы, разумеется, но она хорошо документирована.


 
Zeqfreed ©   (2008-07-11 10:28) [30]

Есть libsigc++ — Typesafe callback system for standard C++


 
@!!ex ©   (2008-07-11 10:32) [31]

Наш программист сказал, что делегаты - это то что нам нужно.
Брать чужие разработки смысла нет, у нас уже есть редактор VCL подобный, набор компонентов.
Только callback еще не работает.


 
guav ©   (2008-07-11 11:34) [32]

> [31] @!!ex ©   (11.07.08 10:32)
> Брать чужие разработки смысла нет, у нас уже есть редактор
> VCL подобный, набор компонентов.

Не понимаю. Почему бы не взять готовое слот-сигнальное решение, если действительно нужны делегаты. Boost.Signals например.


>              m_NewGameButton = new cButton (...);
>              m_QuitButton = new cButton (...);

Вот здесь, например, сильно желательны смарт-поинтеры. Причём, как и во многих случаях, скорее будут нужны scoped_ptr или shared_ptr, а не std::auto_ptr. Тоже свой велосипед изобретать будете ?



Страницы: 1 вся ветка

Текущий архив: 2008.08.31;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.014 c
3-1204722118
Ega23
2008-03-05 16:01
2008.08.31
Оптимизация запроса - правильно ли я понимаю?


3-1204541204
Igor_34
2008-03-03 13:46
2008.08.31
Правильно - 0 или Null ?


1-1195976284
DmT
2007-11-25 10:38
2008.08.31
Как засунуть форму в TaskBar, ввиде отдельной панельки


4-1195513824
Alx2k
2007-11-20 02:10
2008.08.31
Окно выбора значка


2-1216927230
self.name
2008-07-24 23:20
2008.08.31
компонент внутри компонента...