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

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

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

« : 20-10-2006 20:05 » 

Возник у меня такой вопрос. Вообще callback функция - это функция вызываемая системой в ответ на какое-то событие или действие. А есть ли возможность реализовать то же самое для собственного класса?

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

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

Есть другой класс, который реализует работу с экраном. Он использует класс маски и знает как работать с экраном. Можно ли в нем описать закрашивание пикселей и передать указатель на этот код классу маски, чтобы при выполнении метода маски "наложить" вызвался бы код закрашивания пикселей. То есть код закрашивания пикселей можно назвать callback функцией, а код класса маски - системой, которая обеспечивает callback.

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

Прошу подсказать решение.

З. Ы. Я вижу, что можно использовать наследование, но это чересчур громоздко, при этом надо использовать множественное наследование и вообще такое решение мне кажется не совсем правильным.

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

Мне кажется, что существует более простое решение, но мои познания в С++ видимо недостаточны, чтобы его найти Жаль
Записан

- А Вы сами-то верите в привидения?
- Конечно, нет, - ответил лектор и медленно растаял в воздухе.
Джон
просто
Администратор

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

« Ответ #1 : 20-10-2006 22:00 » 

Я думаю ты правильно мыслишь насчёт callback функции. Если честно, я не совсем понял твои заморочки с экраном. Но если я правильно понимаю, у тебя есть нечто работающее стандартно, но какая-то определённая часть должна обрабатываться за пределами этого "нечто". Попробую пояснить на примере - контролл отображающий список. Он сам по себе работает - отображает информацию в столбиках. Но вот тебе надо отсортировать инфу в столбиках. Откуда список должен знать как именно сортировать? Что у тебя? ЦИфры? Имена? Даты?
Поэтому при инциаллизации в список передаётся указатель на callback функцию, которая вызывается самим списком. Эта функция получает два параметра - два айтема списка, в качестве возвращаемого значения допустим 1,0,-1. Те как это всё работает. В список встроен алгоритм сортировки для этого ему надо сравнивать два айтема - меньше, больше, равно - -1, 1, 0. Тогда список вызывает функцию с этими двумя параметрами и использует возвращаемое значение в своём алгоритме сортировки. Рализация тела функции находится за пределами списка - единственное требование соблюдение типа функции. Таким образом, можно реализовать сравнение для конкретного типа информации не затрагивая сам список.

Вот так это можно реализовать в коде, может быть объявление типа и CALLBACK  будут описаны по разному в разных средах, я работаю в VS .NET 2003, в ней это делается примерно так

Код:

// определяем тип функции - она получит целый тип и вернёт его
typedef int (CALLBACK *MYCBPROC)(int);

// основной класс вызывающий CBF
class A
{
public:
A() { m_pf = NULL; }
~A() {}

// имеет смысл сохранить указатель на ф-ю
void Init(MYCBPROC pf)
{
m_pf = pf;
}

void Action()
{
if(m_pf)
{
for(int i=0; i<10; i++)
{
// вызовем ф-ю передав в неё "НАШ" параметр
int nRet = m_pf(i);
//  что-нить сделаем в возвращённым значением nRet
}
}
}

private:
MYCBPROC m_pf;
};

class Sum
{
public:
Sum() {}
~Sum() {}

void Action()
{
A a;
a.Init(MyFunc);
a.Action();
}

// реализация CBF
int MyFunc(int n)
{
int nRet = 0;
// что-нить сделать с полученным параметром
// nRet = f(n);
nRet = n+n;
//  и вернуть результат
return nRet;
}
};


class Mult
{
public:
Mult() {}
~Mult() {}

void Action()
{
A a;
a.Init(MyFunc);
a.Action();
}

// реализация CBF
int MyFunc(int n)
{
int nRet = 0;
// что-нить сделать с полученным параметром
// nRet = f(n);
nRet = n*n;
//  и вернуть результат
return nRet;
}
};

...

Sum s;
s.Action();

Mult m;
m.Action();


За безошибочность кода не ручаюсь - не проверял, тк вопрос задан в теме по общему С++, но идея такая. Теперь сам смотри, подойдёт тебе это, или нет.


зы Посмотрел и решил "усилить" прмер. Два класса используют один и тот же алгоритм перебора данных, но реагируют по разному. Я понимаю, что пример примитивный и данную задачу можно решить по другому. Я просто хотел показать, как раотает callback функция.

зы зы Аааа вот ещё забыл сказать самое главное, может не все знают - в С++ имя ф-ции является указателем на ф-ю.
« Последнее редактирование: 20-10-2006 22:19 от Джон » Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Scorp__)
Молодой специалист

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

« Ответ #2 : 21-10-2006 08:01 » 

Джон, у меня сейчас нету времени проверить к сожалению. Но... у тебя это компилируется? Я просто думал, что так не получится, потому что метод компилируется по соглашению _thiscall и сигнатура функции будет отличаться в результате.

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

PS Про имя функции и указатель на нее я знал )
Записан

- А Вы сами-то верите в привидения?
- Конечно, нет, - ответил лектор и медленно растаял в воздухе.
Джон
просто
Администратор

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

« Ответ #3 : 21-10-2006 10:54 » 

Scorp__), я не компилировал именно этот код. Скажу более, именно ТАК я никогда не использовал. Поэтому пример скорее всего неудачный. Скажу только как я CBF использовал. Передача ф-ции в CreateThread

WINBASEAPI HANDLE WINAPI CreateThread ( IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN SIZE_T dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId )

где

typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE

где

typedef VOID WINAPI* PFIBER_START_ROUTINE ( LPVOID lpFiberParameter )

вот так выглядит в коде:

DWORD dwThread;
   try
   {
      HANDLE hThread = CreateThread(NULL, 0, RunThread, NULL, 0, &dwThread);

а вот так я определяю саму RunThread

DWORD WINAPI RunThread(LPVOID lpParameter);

Другой пример, ща более точно глянул в MSDN для MFC СListCtrl, вчера писал по памяти.

BOOL SortItems (PFNLVCOMPARE pfnCompare, DWORD_PTR dwData)
где
typedef int CALLBACK* PFNLVCOMPARE (LPARAM, LPARAM, LPARAM)
реализация
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);

Вот пример из MSDN как она используется:

Код:
Example

// Sort the item in reverse alphabetical order.
static int CALLBACK
MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
   // lParamSort contains a pointer to the list view control.
   // The lParam of an item is just its index.
   CListCtrl* pListCtrl = (CListCtrl*) lParamSort;
   CString    strItem1 = pListCtrl->GetItemText(lParam1, 0);
   CString    strItem2 = pListCtrl->GetItemText(lParam2, 0);

   return strcmp(strItem2, strItem1);
}

void snip_CListCtrl_SortItems()
{
   // The pointer to my list view control.
   extern CListCtrl* pmyListCtrl;

   // Sort the list view items using my callback procedure.
   pmyListCtrl->SortItems(MyCompareProc, (LPARAM) pmyListCtrl);
}

Ну а сохранение указателей на ф-ю использую сплошь и рядом, когда загружаю dll динамически и получаю из них ф-ции.

Пример:

m_pfGetPortDllInfo = (GETPORTDLLINFOPROC)GetProcAddress(hinstLib, "GetPortDllInfoA");

где

class CPortDll 
{
...
private:
   INITPORTPROC m_pfInitPortDll;
   GETPORTDLLINFOPROC m_pfGetPortDllInfo;
   HASDLLPORTPROC m_pfHasDllPort;
};

типы определены как

typedef CPort* (*INITPORTPROC)(int,char*);
typedef BOOL (*GETPORTDLLINFOPROC)(LPPORTDLLINFO);
typedef BOOL (*HASDLLPORTPROC)(int);

Ну и тд Может насчёт сигнатуры ты и прав. Но это работает, в смысле - должно работать Ага . У меня ща тоже не очень со временем. Ты на какой системе работаешь? Вечерком могу глянуть для моей. В смысле примерчик сделаю, чтоб компилился. Ага
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Джон
просто
Администратор

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

« Ответ #4 : 21-10-2006 11:21 » 

Короче не выдержал я.

Вот так всё работает: соглашение __cdecl и конечно же ф-я должна быть статиком - сорри эт я забыл.

Код:
typedef int (__cdecl *MYCBPROC) (int);

class A
{
public:
A() { m_pf = NULL; }
~A() {}


void Init(MYCBPROC pf)
{
m_pf = pf;
}

void Action()
{
if(m_pf)
{
for(int i=0; i<10; i++)
{
int nRet = m_pf(i);
printf("Return value: %d\n", nRet);
}

}
}

private:

MYCBPROC m_pf;
};


class B
{
public:
B() {}
~B() {}

void Action()
{
A a;
a.Init(MyFunc);
a.Action();
}

static int MyFunc(int n);

};

int B::MyFunc(int n)
{
return n*n;
}

int main(int argc, char* argv[])
{
printf("Sample of callback\n\n");
B b;
b.Action();

return 0;
}

* TestCallback.zip (37.07 Кб - загружено 979 раз.)
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Антон (LogRus)
Глобальный модератор

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


WWW
« Ответ #5 : 22-10-2006 08:46 » 

В вашем решении есть недостаток, что бы вызвать метод класса через указатель необходимо иметь таакже указатель на сам класс, т.е. нужно 2 указателя.

Почему бы не подумать о функторах? Дались вам эти указатели на методы.
вот простейший
struct f
{
   OtherClass &c;
   f(OtherClass &cc):c(cc){}

   void operator()()
   {
      c.f();
   }
}

в класс который должен звать callback отдаём указатель на этот объект и где-то его храним.
хотя я обычно использую boost::function созданный через boost::bind передаю при конструировании объекта(или через SetCallback) на него указатель и наслаждаюсь результатом

рекомендую ознакомится с книгой "Шаблоны C++"(Вандервуд и Н. Джосатис) если точней, то с главой посвещенной Callback функциям или с пятой главой книги "Современный дизайн C++"(Андрей Александреску)
Записан

Странно всё это....
Джон
просто
Администратор

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

« Ответ #6 : 22-10-2006 11:39 » 

LogRus, стоп! В каком решении? Я не пытаюсь решить задачу Scorp__). То, что я накалякал просто пример (скорее всего неудачный, о чём неоднократно уже сказал) функционирования callback функции. А будет ли Scorp__) именно CBF использовать в своей задаче, он должен сам решать. Я просто отреагировал на
Возник у меня такой вопрос. Вообще callback функция - это функция вызываемая системой в ответ на какое-то событие или действие. А есть ли возможность реализовать то же самое для собственного класса?

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

Хотя, хорошо, что ты про них сказал. Scorp__), можешь рассмотреть и их в качестве возможного решения своей задачи. Тебе с твоей колокольни должно быть виднее.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
Scorp__)
Молодой специалист

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

« Ответ #7 : 22-10-2006 16:48 » 

Джон, прошу прощения, приехал только сегодня. Вот дело как раз в статичности такой функции, то есть мне нужен и указатель на сам объект. И функция должна принадлежать объекту все-таки.

LogRus, о функторах я задумывался Улыбаюсь но опыта не хватило, чтобы понять как это сделать. В моем случае нужно средство даже помощнее, чем простейший функтор, потому что хочется не создавать функтор для каждого стороннего класса, при это ведь его придется наследовать от какого-то абстрактного класса функтора, указатель на этот базовый абстрактный класс и должен быть в классе, который зовет callback. В общем-то многовато требований к пользователям моего класса, вызывающего callback.
Спасибо за рекомендованные книги. Надеюсь там описано, что-нибудь подобное моей мечте Улыбаюсь   
Записан

- А Вы сами-то верите в привидения?
- Конечно, нет, - ответил лектор и медленно растаял в воздухе.
Антон (LogRus)
Глобальный модератор

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


WWW
« Ответ #8 : 23-10-2006 04:50 » 

Джон, прошу прощенья был не очень внимателен.
Scorp__), попробуй почитать про boost::function и boost::bind думаю можно обойтись без динамической виртуализации, потому, как тип для разных callback функций будет совпадать, а поведение разное. У нас практически всегда эта связка используется для функторов алгоритмов и для обратных вызовов
Записан

Странно всё это....
Антон (LogRus)
Глобальный модератор

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


WWW
« Ответ #9 : 23-10-2006 05:29 » 

кстати функторы удобно засовывать в контейнер, а потом вызвать их подряд. У нас в одном месте, так и используется.
« Последнее редактирование: 23-10-2006 07:36 от LogRus » Записан

Странно всё это....
Scorp__)
Молодой специалист

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

« Ответ #10 : 23-10-2006 07:35 » new

LogRus, спасибо за совет. Посмотрю обязательно.
Записан

- А Вы сами-то верите в привидения?
- Конечно, нет, - ответил лектор и медленно растаял в воздухе.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines