Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Реализация callback вызова метода класса (делегат) 
V
    Опции темы
Ravenan
Дата 24.5.2006, 12:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 24.5.2006
Где: Киев, Украина

Репутация: нет
Всего: нет



Всем доброе время суток.

Последние пару лет писал на C#, а тут пришлось на С++ вернуться. Не могу реализовать довольно простую как мне кажется вешь, просто не верится что это не выполнимо  smile  Перерыл ваш форум но исчерпывающего ответа не нашел. Суть проблемы - как реализовать на C++ аналог делегата на C#. То есть, у меня допустим есть класс А и класс В. Нужно в метод класса В передать метод класса А чтоб он его вызвал при определенных условиях. Что-то вроде callcack функции, но в ее роли выступает метод. Методы не статические. Плиз подскажите решение или киньте ссылкой. 

ЗЫ Имеется ввиду не передавая в класс B указатель на класс А  

Это сообщение отредактировал(а) Ravenan - 24.5.2006, 15:38
PM MAIL ICQ MSN   Вверх
Mayk
Дата 24.5.2006, 14:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


^аВаТаР^ сообщение>>
****


Профиль
Группа: Участник
Сообщений: 2616
Регистрация: 22.5.2005
Где: за границей разум а

Репутация: 45
Всего: 134



Цитата(Ravenan @  24.5.2006,  12:04 Найти цитируемый пост)
Имеется ввиду не передавая в класс B указатель на класс А 

Придется. Но можно замаскировать.

Ну например вот так:
Код

struct Callback{
    virtual void operator()();
};

template<class C>
struct MemberCallback : public Callback
{   
     typedef void (C::*MemFun)();
     MemFun func;
     C* ptr;

     MemberCallback(C* instance, MemFun member)  :ptr(instance), func(member) { }
    
    virtual void operator()(){
        (ptr->*func)();
    }
};

template<class C>
MemberCallback<C> makeMemberCallback(C* instance, typename MemberCallback<C>::MemFun member) {
 return MemberCallback<C>(instance, member);
}

//
// собсно проверка
//

struct B{
  void foobar(Callback bc){ bc(); } //класс B знать не знает о наличии класса А
};

struct A{
  void d(){
     B b; b.foobar(makeMemberCallback(this, &A::d)); 
  }
};

int main(){
    A a; a.d();
}

(comeau скомпилировал)


То есть мы делаем класс каллбэк, а он сам решает что и кому ему передать управление - ф-ции-члену класса, вызвать какую либо обычную ф-цию или ещё что.

ps. кстати, этот вопрос всплывает не в первый раз. на моей памяти во второй. это не укор автору темы, это небольшая статистика  smile 


    

Это сообщение отредактировал(а) Mayk - 24.5.2006, 14:52


--------------------
 Здесь был кролик. Но его убили.
Человеки < кроликов, йа считаю.
PM MAIL WWW ICQ   Вверх
Ravenan
Дата 24.5.2006, 15:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 24.5.2006
Где: Киев, Украина

Репутация: нет
Всего: нет



Mayk
Большое спасибо  smile  
PM MAIL ICQ MSN   Вверх
Earnest
Дата 24.5.2006, 15:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 53
Всего: 183



В С++ делегатов нет. Пока. Но можно воспользоваться boost::function - это очень гибкий механизм, который наверняка войдет в следующую реинкарнацию STL. 
Это примерно то же самое, что написал Mayk,  но гораздо мощнее - любые параметры, любые возвр. значения и т.д. 


--------------------
...
PM   Вверх
MAKCim
Дата 24.5.2006, 16:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



см. Александреску - обобщенный функтор
тоже любое возвращаемое значение, любые параметры (практически их произвольное количество)
он может инкапсулировать обычный функтор, указатель на функцию, указатель на функцию-член
имхо, обобщеннее трудно себе представить 


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Ravenan
Дата 24.5.2006, 16:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 24.5.2006
Где: Киев, Украина

Репутация: нет
Всего: нет



Mayk
Радость омрачилась ошибкой линкера  smile 

Error    1    error LNK2001: unresolved external symbol "public: virtual void __thiscall Callback::operator()(void)" (??RCallback@@UAEXXZ)    qq.obj    

компилятор - VS2005 
PM MAIL ICQ MSN   Вверх
MAKCim
Дата 24.5.2006, 18:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



Код

struct Callback{
    virtual void operator()() {}
};
template<class C>
struct MemberCallback : public Callback
{   
     typedef void (C::*MemFun)();
     MemFun func;
     C* ptr;
     MemberCallback(C* instance, MemFun member)  :ptr(instance), func(member) { }
    
    virtual void operator()(){
        (ptr->*func)();
    }
};
template<class C>
MemberCallback<C> makeMemberCallback(C* instance, typename MemberCallback<C>::MemFun member) {
 return MemberCallback<C>(instance, member);
}
//
// собсно проверка
//
struct B{
  void foobar(Callback& bc){ bc(); } //класс B знать не знает о наличии класса А
};
struct A{
  void d(){
     B b;
    MemberCallback<A> obj=makeMemberCallback(this,&A::d); 
    b.foobar(obj); 
  }
};
int main(){
    A a; a.d();
}
 


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Ravenan
Дата 24.5.2006, 19:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 24.5.2006
Где: Киев, Украина

Репутация: нет
Всего: нет



MAKCim
Спасибо, теперь все ок!

Вот только думаю раз на такие ухищрения приходится идти - может сама архитектура неправильная?    smile 
У меня есть класс который прнимает сообщения с клавиатуры через DirectInput. Я хотел бы дать ему хеш-таблицу с callback методами для разных клавиш. Самой обработкой естественно другой класс заниматься будет.
Не подскажите - нормальная вообще практика подобная? 
То есть это стандартный подход при проектировании или подобных ситуаций в С++ стоит избегать? 
PM MAIL ICQ MSN   Вверх
MAKCim
Дата 24.5.2006, 21:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



Цитата

Не подскажите - нормальная вообще практика подобная? 

вполне нормальная
сделать что-то типа
Код

std::map<char,functor_t> map_;

и вызывать потом
Код

void some_class::call(char key)
{
    map_[key]();
}
 


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Ravenan
Дата 25.5.2006, 17:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 24.5.2006
Где: Киев, Украина

Репутация: нет
Всего: нет



MAKCim
Прошу помощи в решении еще одной возникнувшей проблемы  smile 

Все прекрасно работает если метод вызывается непосредственно там куда его передали вроде
Код

void foobar(Callback& bc)

    bc(); 
}

А если bc сохранить в каком-то поле класса и вызвать потом из другого метода, то приложение вываливается.
Код

struct B
{
    Callback *p;
    
    void foobar(Callback& bc)
    { 
        p = &bc;
    }

    void foobar1()
    { 
        (*p)();
    }
};

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

Объясните пожалуйста причину подобного побочного еффекта  smile    

Это сообщение отредактировал(а) Ravenan - 25.5.2006, 17:33
PM MAIL ICQ MSN   Вверх
likehood
Дата 25.5.2006, 17:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


666
**


Профиль
Группа: Участник
Сообщений: 536
Регистрация: 21.12.2005

Репутация: 8
Всего: 24



Цитата(Ravenan @  25.5.2006,  18:15 Найти цитируемый пост)
    void foobar(Callback& bc)    
    {  
        p = &bc;    
    }    
    void foobar(Callback& bc)    
    {  
        (*p)();    
    }


Может у функций все же разные имена? 
PM MAIL   Вверх
Ravenan
Дата 25.5.2006, 17:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 24.5.2006
Где: Киев, Украина

Репутация: нет
Всего: нет



baronp
да, сорри, подправил 
PM MAIL ICQ MSN   Вверх
likehood
Дата 25.5.2006, 17:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


666
**


Профиль
Группа: Участник
Сообщений: 536
Регистрация: 21.12.2005

Репутация: 8
Всего: 24



а как ты вызываешь 1-ю функцию? 
PM MAIL   Вверх
Ravenan
Дата 25.5.2006, 17:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 11
Регистрация: 24.5.2006
Где: Киев, Украина

Репутация: нет
Всего: нет



Код

class A
{
private:
    B bInst;

    void handler()
    {
    }


public:
    A()
    {
        bInst->foobar(MemberCallback<A>(this, &A::handler));
    }

    void Call()
    {
        bInst->foobar1();
    }
};
  

Это сообщение отредактировал(а) Ravenan - 25.5.2006, 17:49
PM MAIL ICQ MSN   Вверх
likehood
Дата 25.5.2006, 17:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


666
**


Профиль
Группа: Участник
Сообщений: 536
Регистрация: 21.12.2005

Репутация: 8
Всего: 24



При вызове foobar в кострукторе A() ты создаешь временный объект, который разрушается после выхода из foobar. Выход: в классе B вместо указателья обявить переменную, но тогда нужен default-конструктор в MemberCallback и там же нужен copy-коструктор.

Добавлено @ 18:01 
На счет замены указателья это я погорячился. Надо в первый foobar передавать не ссылку, а указатель, при этом объект MemberCallback  в конструкторе А придется создавать динамически. Тогда придется думать об удалении этого объекта (возможно auto_ptr). 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1292 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.