Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Объект как функция  (Прочитано 9150 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« : 24-11-2008 10:01 » 

Имеется в некой библиотеке некая функция, которая принимает аргументом другую функцию для последующего callback-вызова.

Допустим:
Код: (C++)
typedef void (*Handler)();

void registerHandler(Handler handler);

Очень хочется передать туда такую функцию, внутри которой был бы известен контекст её исполнения (объект). Просто создать класс с оператором "()" недостаточно, поскольку тип MyClass::operator() не соответствует типу Handler.

Адаптеры STL типа mem_fun построены так, что при вызове результата этих адаптеров типа mem_fun_t требуется передать аргументом объект.

Можно попробовать убрать этот аргумент, но адаптеры STL типа bind1st, если я правильно понимаю, возвращают объект класса binder1st - и это возвращает к исходной ситуации.

Можно ли исхитриться? Улыбаюсь
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 24-11-2008 12:33 » 

Есть мнение, что никак.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 24-11-2008 12:44 » 

Я так понимаю, вариант "синглетон" не подходит?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 24-11-2008 13:44 » 

Вад, это частный случай. Общий случай - много объектов одного класса. Нужно что-то вроде замыкания (closure), известного из некоторых скриптовых языков.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 24-11-2008 13:50 » 

dimka, ну, если библиотека не предполагает многопоточного использования - то подошло бы вполне: устанавливаешь контекст и работаешь.

А этот handler - он дальше как используется, просто происходит callback при каждом событии для всех подписавшихся? или?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 24-11-2008 15:28 » 

Вад, это уже детали пошли Улыбаюсь

На самом деле, как тут выяснилось, вместе с регистрацией обработчика передаётся ID контекста, который затем подаётся аргументом в обработчик. Так что в моей конкретике проблемы нет вовсе. И остаётся лишь исходный вопрос.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
npak
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 24-11-2008 18:09 » 

Есть такая возможность, даже две.

1. Число одновременно зарегистрированных хендлеров конечно и не превосходит некоторой константы, например MAX_HANDLER. Тогда создаешь массив контекстов contexts[MAX_HANDLER] и массив функций handlers[MAX_HANDLER], причем в функции handlers[ i ] жестко прописано использовать в качестве контекста contexts[ i ]

Сценарий использования - выбираешь какое-то значение i, устанавливаешь значение контекста contexts[ i ], затем регистрируешь handlers[ i ].

2. второй способ несколько сложнее в реализации - можно на ходу гененрировать код хендлера. Но это получится уже самодифицирующая программа, что не есть хорошо.
« Последнее редактирование: 24-11-2008 18:13 от npak » Записан

UniTesK -- индустриальная технология надежного тестирования.

http://www.unitesk.com/ru/
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 24-11-2008 20:44 » 

npak, не понял. Контекст по сути своей представляет экземпляр некой структуры или класса. Обработчик - это функция. Функция одна, класс один - в случае синглетона вопроса нет. А вот когда экземпляров много, то как определить, с каким контекстом работать этой единственной функции?

Если отдельно описывать функцию для каждого экземпляра, то никакой динамики в программе не будет - это вариант с N синглетонами.


Предлагаемый выше вариант с предварительной установкой контекста в некую глобальную переменную в моём конкретном случае не подходил, поскольку вызов обработчиков происходил не в порядке их регистрации, а в порядке, определяемом внутренней логикой библиотеки. И можно было зарегистрировать несколько обработчиков.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #8 : 24-11-2008 22:40 » 

dimka, Глобальный STL::map. Если он не нравится, можно и в класс синглетон его положить. В нем делаеш связку ID - экземпляр объекта. В принципе почти тоже самое и предложил npak. Теперь в своей callback функции, судя по твоим словам, ты уже знаеш ID. Первым делом обрашаешся к синглетону и по ID получаеш ссылку на экземпляр класса. Далее уже дело техники. Если ID формируется тобой, а не библиотекой и имеет тип int, можно конечно через него и передавать сам адрес на экземпляр класса. Правда не всегда такой подход будет работать.
Сейчас посмотрел на твое определение функции
Код:
typedef void (*Handler)();
Интересно, как библиотека ID передает в callback функцию?
« Последнее редактирование: 24-11-2008 22:44 от Finch » Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #9 : 25-11-2008 07:11 » 

А может всё же функтор поиспользовать, чем огород городить.
Периодически приходится всякие функторы хранить в контейнере, внутри объекта и еще где-то, куда-то меня лично страивает
В качестве функтора, использую boost::function, как результат boost::bind
Код:
typedef boost::function< void ()> Handler;

void registerHandler(Handler handler)
{
std::cout << "registerHandler\n" ;
handler();
}

struct A
{
doit(){ std::cout << "A::doit\n" ;}
};

struct B
{
doit2(){ std::cout << "A::doit2\n" ;}
};

A a;
B b;
registerHandler(boost::bind(&A::doit, &a));
registerHandler(boost::bind(&B::doit2, &b));
Записан

Странно всё это....
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 25-11-2008 10:44 » 

Цитата: Finch
Теперь в своей callback функции, судя по твоим словам, ты уже знаеш ID.
Я же написал, что поскольку на самом деле есть ID, то проблемы нет. Именно через map и сделали сразу же. Я говорю, что остаётся исходный вопрос - когда нет никаких ID.

Цитата: LogRus
typedef boost::function< void ()> Handler;

void registerHandler(Handler handler)
{
   std::cout << "registerHandler\n" ;
   handler();
}
Так не пойдёт. Если бы была моя библиотека, я бы вообще подписывал не функцию, а объект с известным интерфейсом - по шаблону "Наблюдатель". В чужой библиотеке я не могу переопределять тип Handler так, как мне вздумается. Я должен пользоваться таким типом, который есть.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #11 : 25-11-2008 11:16 » 

эхъ
объясните мне, темноте, проблему на пальцах, не могу вникнуть (
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 25-11-2008 13:07 » 

Есть возможность сделать callback твоих функций (разных) из чужой библиотеки в ответ на какие-то события (разные). Твои функции могут быть подписаны на callback средствами библиотеки. При подписывании каждой твоей функции ты передаёшь дополнительные параметры (например, тип события, в ответ на которое будет вызываться подписываемая функция). Но когда происходит callback твоей функции, у неё в параметрах нет никакой дополнительной информации.

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

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

Спрашивается, возможно ли это?

Например:
Имеется.
Код: (C++)
// Чужая библиотека.
enum EventType { Event1, Event2 };
typedef void (*Handler)();
void registerHandler(Handler handler, EventType type);

Нужно писать:
Код: (C++)
// Твой код.
void handleEvent1() { cout << "Event1" << endl; }
void handleEvent2() { cout << "Event2" << endl; }

// Где-то ниже подписка.
registerHandler(handleEvent1, Event1);
registerHandler(handleEvent2, Event2);

Чего хочется:
Код: (C++)
// Твой код.
class MyHandler
{
private:
  EventType event;
  string data;
public:
  MyHandler(EventType event): event(event) {}
  void setData(string &data) { this->data = data; }
  EventType getEvent() { return this->event; }
  void operator()() { cout << this->data << endl; }
};

typedef list<MyHandler> Handlers;
Handlers handlers;
handlers.push_back(MyHandler1(Event1));
handlers.push_back(MyHandler1(Event2));
handlers[0].setData("Event1");
handlers[1].setData("Event2");

// Где-то ниже подписка.
for(Handlers::iterator ptrHandler = handlers.begin(); i != handlers.end(); ++i)
  registerHandler(*ptrHandler, ptrHandler->getEvent());
Цель - добиться динамики в подписке.

Но так нельзя, потому что тип void MyHandler::operator()() не соответствует типу void Handler().
« Последнее редактирование: 25-11-2008 13:10 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #13 : 25-11-2008 13:50 » 

Я бы сделал так:
Код: (C)
void event(int event_id) { ... }

void event_1(void) { event(1); }
void event_2(void) { event(2); }
void event_N(void) { event(N); }
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 25-11-2008 13:58 » 

RXL, опять же, частный случай, если есть EventType с обозримым количеством значений. А если там параметр типа int или того хуже double?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #15 : 25-11-2008 14:11 » 

Значения извесны при компиляции или множество динамическое?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 25-11-2008 14:30 » 

Цитата: RXL
Значения извесны при компиляции или множество динамическое?
Читается из config-файла.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
zubr
Команда клуба

by
Offline Offline
Пол: Мужской

« Ответ #17 : 25-11-2008 18:20 » 

dimka, посмотри на интерфейсную реализацию метода Invoke интерфейса IDispatch Windows. Там как раз этот метод позволяет реализовывать практически любое событие с различным количеством и типом параметров.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 25-11-2008 20:26 » 

zubr, а к чему это? Это же метод объекта и, соответственно, контекст его исполнения определён. Чего не скажешь о функции.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
zubr
Команда клуба

by
Offline Offline
Пол: Мужской

« Ответ #19 : 26-11-2008 07:54 » 

Цитата
Есть возможность сделать callback твоих функций (разных) из чужой библиотеки в ответ на какие-то события (разные). Твои функции могут быть подписаны на callback средствами библиотеки. При подписывании каждой твоей функции ты передаёшь дополнительные параметры (например, тип события, в ответ на которое будет вызываться подписываемая функция). Но когда происходит callback твоей функции, у неё в параметрах нет никакой дополнительной информации.
Я к тому, что параметры метода Invoke позволяют передавать в callback-функции любые параметры и с любыми типами. То есть если использовать принцип передачи параметров данного метода, то вообще можно отказаться от регистрации событий. К примеру:
Код:
void MyEventClass::MyEvent1 (char* Param1, int Param2)
{
      .....................
}

void MyEventClass::DispatchEvent (DISPPARAMS* pDispParams)
{
      if ((pDispParams->cArgs == 2) &&  (pDispParams->rgvarg[0].vt == VT_BSTR) && (pDispParams->rgvarg[1].vt == VT_I4))
      MyEvent1(pDispParams->rgvarg[0], pDispParams->rgvarg[1]);
      else
      ............................
}

IDispatch::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
                                DISPPARAMS* pDispParams, VARIANT* pvarResult,
                                EXCEPINFO*  pExcepInfo,  UINT* puArgErr)
{
      DispatchEvent (pDispParams);

}
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 26-11-2008 12:02 » 

Цитата: zubr
Я к тому, что параметры метода Invoke позволяют передавать в callback-функции любые параметры и с любыми типами. То есть если использовать принцип передачи параметров данного метода, то вообще можно отказаться от регистрации событий.
Как я могу менять набор параметров, когда тип обработчика и регистратора события определён в чужой библиотеке? Если бы чужая библиотека была настроена на вызов Invoke, то да. Так она реализована другим способом.

Понятно, что если тип параметра "void *", то засунуть туда я могу всё, что угодно - лишь бы потом суметь это разобрать.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines