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

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

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« : 16-10-2004 12:21 » 

Вот, к примеру, такой кусок кода:
Код:
typedef void (CALLBACK aNOT) (int* ii);
typedef aNOT* P_NOTa;

class zzz
{
public:
    void CALLBACK zasd (INT *f) { /**/ } // Line X1
};

BOOL aaa(P_NOTa ap)
{
    return TRUE;
}

void CALLBACK asd (INT *f)   // Line X2
{
    // stuff
}

void tmpf()
{
    zzz z;
    aaa(asd);  // Line X3
    aaa(z.zasd); // Line X4
}
На строчке X4 возникает ошибка "error C2664: 'aaa' : cannot convert parameter 1 from 'void (int *)' to 'void (__stdcall *)(int *)'"
На строчке X3 ошибки НЕ ВОЗНИКАЕТ!
Видимо, проблема в том, что zasd() является функцией-членом?
Объявления в строчках X1 и X2, в принципе, одинаковые.
Вопрос: как мне передать в качестве параметра функцию zasd?
Желательно не менять типы в typedef и объявление функции aaa.
Уже третий час сижу, закипаю. Жаль
« Последнее редактирование: 02-12-2007 10:40 от Алексей1153++ » Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Алексей++
глобальный и пушистый
Глобальный модератор

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


« Ответ #1 : 16-10-2004 17:39 » 

baldr, никогда не разбирался с указателями на функции ( надо будет как-нибудь разобраться:) ) и не совсем понимаю, как вызвать функцию по её указателю, но вот это компилятор проглотил:

Код:
typedef void (CALLBACK aNOT) (int* ii); 
typedef aNOT* P_NOTa;

class zzz
{
public:
    void CALLBACK zasd (INT *f) { /**/ } // Line X1
    void gluk(int __cdecl (P_NOTa)){ /*здесь я не знаю, что делать*/}
};

BOOL aaa(P_NOTa ap)
{
    return TRUE;
}

void CALLBACK asd (INT *f)   // Line X2
{
    // stuff
}

void tmpf()
{
    zzz z;
    aaa(asd);  // Line X3
    z.gluk(aaa);
    //aaa(z.zasd); // Line X4
}

то есть указатель на aaa передаю в класс zzz, а дальше - фиг его знает  :?
« Последнее редактирование: 02-12-2007 10:41 от Алексей1153++ » Записан

Finch
Спокойный
Администратор

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


« Ответ #2 : 16-10-2004 20:50 » 

baldr, Я не знаю точно для VC++. Для билдера запрешено создавать в классе возрашаемые функции. Что то связано с однообразием вызовов функций в классе. Все функции в классе создаются с моделью _fastcall. И для этого служит специальный макрос, который преврашает функцию в возрашаемую, а затем нужно закрывать этот макрос. Давно я это делал, сейчас точно не помню Улыбаюсь
Записан

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

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


« Ответ #3 : 17-10-2004 15:20 » 

baldr, Если есть у тебя исходники всех объектов MFC, посмотри как реализовано обрашшение к WinAPI функции RegisterClass (может быть RegisterClassEx). В эту функцию дается параметр в виде структуры WNDCLASS. Один из элементов этой структуры (WNDPROC lpfnWndProc) возврашаемая функция.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Mfcer__
Команда клуба

ru
Offline Offline

« Ответ #4 : 17-10-2004 15:34 » 

попрбуй описать функцию в классе статически - static
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #5 : 18-10-2004 06:38 » 

baldr, так как у тебя написано компилятор не понимает, что это функция статическая, а поинтер только на статический член класса можно сохранить и вызвать без объекта (хотя с объектом можно и thiscall), так что у тебя два пути

1 пиши так
Код:
typedef void (CALLBACK aNOT) (int* ii); 
typedef aNOT* P_NOTa;

class zzz
{
public:
    static void CALLBACK zasd (INT *f) { /**/ } // Line X1
};

BOOL aaa(P_NOTa ap)
{
    return TRUE;
}

void CALLBACK asd (INT *f)   // Line X2
{
    // stuff
}

void tmpf()
{
    zzz z;
    aaa(asd);  // Line X3
    aaa(z.zasd); // Line X4
}

2 либо так
Код:
class zzz;
typedef void (CALLBACK zzz::*aNOT ) (int* ii);

class zzz
{
public:
    void CALLBACK zasd (INT *f) { /**/ } // Line X1
};

BOOL aaa(aNOT func)
{
    return TRUE;
}

void CALLBACK asd (INT *f)   // Line X2
{
    // stuff
}

void tmpf()
{
    zzz z;
//    aaa(asd);  // Line X3 //  ТАК БОЛЬШЕ НЕ ПРОКАТИТ!!!
    aaa( &zzz::zasd ); // Line X4
}
« Последнее редактирование: 02-12-2007 10:43 от Алексей1153++ » Записан

С уважением Lapulya
Pu
Большой босс

ru
Offline Offline
78


« Ответ #6 : 18-10-2004 08:28 » 

baldr, А ты работаешь в callback функции с членами класса не статик?
Записан

Насколько я опытен? Достаточно, чтобы понимать, что дураков нельзя заставить думать по–другому, но недостаточно, чтобы отказаться от попыток это сделать.
(с) Артур Джонс
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #7 : 18-10-2004 08:39 » 

baldr, да ... действительно (заяц, отличный вопрос!!!) а что ты собственно хочешь т.е. почему ты делаешь именно так, а не по другому...
Записан

С уважением Lapulya
Mad
Гость
« Ответ #8 : 18-10-2004 10:49 » 

baldr, придется тебе делать так:
Код:
typedef void (CALLBACK aNOT) (int* ii); 
typedef aNOT* P_NOTa;

class zzz
{
public:
    void zasd (INT *f) { /**/ } // Line X1
};

BOOL aaa(P_NOTa ap)
{
    return TRUE;
}

BOOL aaa(zzz& z)
{
    return TRUE;
}

void CALLBACK asd (INT *f)   // Line X2
{
    // stuff
}

void tmpf()
{
    zzz z;
    aaa(asd);  // Line X3
    aaa(z); // Line X4
}
« Последнее редактирование: 02-12-2007 10:44 от Алексей1153++ » Записан
Finch
Спокойный
Администратор

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


« Ответ #9 : 18-10-2004 14:59 » new

baldr, Это крайний случай. Сделай через функцию друга.
1. Ты получиш полный доступ ко всем переменным класса.
2. При этом твоя функция не будет связана с форматами вызова функций в классе.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #10 : 01-11-2004 16:30 » 

Желательно делать в классе... И статической не хочется...
Это будет диплом. А для диплома я делаю еще описание в виде UML-диаграмм. И на диаграммах не очень понятно как показать глобальные переменные и вообще отдельные статические функции.
Хочу показать класс... А эта функция очень важна...
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Anchorite
Гость
« Ответ #11 : 01-11-2004 21:03 » 

Без static  здесь получается полный бред:
Если функция является членом класса и не является static, то внутри доступны ВСЕ поля ЭКЗЕМПЛЯРА класса и указатель this ЭКЗЕМПЛЯРА класса, для которого ее вызывают.
А у тебя получается что ты хочешь вызвать функцию для несуществующего экземпляра класса.

Так что или stattic перед void CALLBACK zasd (INT *f) { /**/ } // Line X1
или
Код:
BOOL aaa(zzz z)
{
    int n = 0; // Для примера
    return z.zasd(n);
}
« Последнее редактирование: 02-12-2007 10:45 от Алексей1153++ » Записан
npak
Команда клуба

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

« Ответ #12 : 02-11-2004 11:10 » 

baldr, функция-член класса (не статическая), объявленная с одним параметром, на самом деле получает два параметра -- указатель на объект этого класса и собственно аргумент.

Можно взять указатель на метод, для этого можно воспользоваться конструкцией
Код:
&zzz::zasd

Но это НЕ ЕСТЬ указатель на функцию.  Это специальная синтаксическая конструкция, которую можно использовать только вызове функции совместно с объектом класса.  Например,
Код:
typedef void (CALLBACK zzz::*P_ZZZ_ZASD);
P_ZZZ_ZASD p_zasd = &zzz::zasd;
z.*p_zasd(&n);

Отдельное выражение
Код:
z.*p_zasd
нельзя использовать как указатель на функцию.

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

Так вот, для этой задачи уже достаточно давно придумали технику, получившую названия "Command" и "Delegate"
Код:
class AbstractDelegate {
public:
    virtual void operator () (int *) = 0;
};

class NegationDelegate : public AbstractDelegate {
public:
    void operator () (int * n) {
        // Some stuff
    }
};

BOOL aaa(P_NOTa ap)
{
    return TRUE;
}

BOOL aaa(AbstractDelegate & ad) {
    int n;
    ad(&n);
    return TRUE;
}

void CALLBACK asd (INT *f)   // Line X2
{
    // stuff
}

void tmpf()
{
    NegationDelegate nd;
    aaa(asd);  // Line X3
    aaa(nd);
}

В приведённом куске кода создаётся абстрактный класс AbstractDelegate, содержащий оператор функционального вызова с аргументом (int *).  Для примера показан потомок делегата, в котором реализован функциональный вызов.  Дан пример функции, которая пользуется делегатом для выполнения необходимой операции.  

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

Более подробно приём описан здесь: http://www.codeguru.com/Cpp/Cpp/cpp_mfc/article.php/c4119/
« Последнее редактирование: 02-12-2007 10:46 от Алексей1153++ » Записан

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

http://www.unitesk.com/ru/
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #13 : 02-11-2004 12:16 » 

baldr,
Цитата

А для диплома я делаю еще описание в виде UML-диаграмм. И на диаграммах не очень понятно как показать глобальные переменные и вообще отдельные статические функции.
Хочу показать класс... А эта функция очень важна...

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

    void CALLBACK zasd (INT *f) { /**/ } // Line X1

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

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

С уважением Lapulya
Finch
Спокойный
Администратор

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


« Ответ #14 : 10-11-2004 17:45 » 

Сегодня наткнулся на статью на подобную тему http://winapi.by.ru/winapi/index.htm-article=patterns.htm .
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #15 : 11-11-2004 08:18 » 

:new_goss:
lapulya, эта функция является одним из важнейших элементов реализации алгоритма. Поэтому я организовал для нее отдельный класс, что, в целом, правильно, так как она все равно должна работать с различными объектами, созданными в программе. Если делать функцию отдельно, глобальной, то тогда надо для нее инициализировать туеву хучу глобальных переменных.  :new_shot:  Про ООП тогда можно забыть. Так у меня было сделано ДО этого. Было плохо.
  Что касается примера npak, то он интересен. Но тут возникает проблема. Уже есть класс, который реализует вызов этой CALLBACK-функции. И его менять нельзя из принципа. Вот оба typedef и функция aaa(...)  из моего первоначального примера как раз части, которые менять нельзя. Жаль
:l_lick_lick:  Сейчас сделал в классе статическую функцию. Из-за нее пришлось делать все переменные-члены класса тоже статическими. По-моему, получилось даже еще хуже, чем при использовании глобальных переменных.   :new_mpr:
  И все равно мне до сих пор не понятно почему CALLBACK-функция должна быть static?? :l_smile: Есть конкретный экземпляр класса, ссылку на его функцию я передаю... Что еще надо?
Finch, спасибо за ссылку. Но там пишется что оно так, но не пишется почему.
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
npak
Команда клуба

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

« Ответ #16 : 11-11-2004 09:24 » 

baldr, сравни:

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

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

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

Это нормальное дело для функциональных языков, но в С не реализовано (и унаследовано в С++).  Ты можешь попробовать генерить тело функции самостоятельно.

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

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

http://www.unitesk.com/ru/
trahomoter
Гость
« Ответ #17 : 11-11-2004 10:14 » 

точно знаю, что на rational rose enterprise edition  можно указывать тип переменной, а ты наверняка на нём uml делаешь.
Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #18 : 11-11-2004 11:06 » 

npak, не с чем поспорить.  Показываю язык
Но если мы вызываем: class1.func1(&class2.func2()); будет не то же самое? Ссылаемся на функцию-член.
А class1.func3(func4); ? Разве описание func1 и func3 будет разным?
[вообще-то, что-то в этом есть... наверное, будет...  :? ]

То есть, подведем небольшой итог: если не переписывать функцию aaa(...) из примера, то выхода два: сделать функцию zasd(...) либо глобальной, либо статической в классе? Я прав? Третьего способа нет?

trahomoter, да, это rational rose, ясен пень. Но при чем тут переменная?
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
npak
Команда клуба

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

« Ответ #19 : 11-11-2004 11:48 » 

baldr, &class2::func -- это указатель на метод func в классе class2.
&::func -- это указатель на глобальную функцию func.

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

Цитата
если не переписывать функцию aaa(...) из примера, то выхода два: сделать функцию zasd(...) либо глобальной, либо статической в классе? Я прав? Третьего способа нет?


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

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

http://www.unitesk.com/ru/
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #20 : 11-11-2004 12:28 » 

npak, все понял. Спасибо.

Не нравилось мне это статическое определение...
Но lapulya, спасибо за подсказку про шаблон Singleton. Сделал по нему. Теперь относительно удовлетворен.
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines