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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Си callback и экземпляр класса 
V
    Опции темы
Cheloveck
Дата 27.11.2009, 14:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

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



Извиняюсь за банальную тему, но полазив по интернетам ничего толкового не нашёл.

Итак. Есть закрытая библиотека на Си, одна из функций которой возвращает результат своей деятельности в колбек. У меня в программе её вызывает, естественно, метод класса. Задача - получить данные в конкретный экземпляр. В Сети самый распространённый вариант - использование статических методов в качестве колбека, но тогда нет доступа к членам конкретного объекта.

Теперь на примере. Допустим мы имеем потенциальную библиотеку с таким вот кодом
Код

typedef void (*callback_t)(wchar_t *);

void funct(callback_t callback)
{
    callback(L"Hello");
}

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

В нашей программе имеется некий класс
Код

class tester
{
public:
    tester(int id) :
      _id(id),
      _first(L"I'm first"),
      _second(L"I'm second")
    {
    }

    void run()
    {
        if(_id == 1)
            funct(/*Что-то такое &tester::this_callback но не это*/);
        else
            funct(/*Что-то такое &tester::this_callback2 но не это*/);
    }

    void this_callback(wchar_t * str)
    {
        MessageBoxW(NULL, str, _first, 0);
    }

    void this_callback2(wchar_t * str)
    {
        MessageBoxW(NULL, str, _second, 0);
    }
private:
    int _id;
    wchar_t *_first;
    wchar_t *_second;
};

Для наглядности показываем сообщение. Часть данных берётся из конкретного объекта, часть из параметра колбека.

Используется примерно так
Код

void ButtonClick()
{
    tester test(1);
    test.run();
    tester test2(2);
    test2.run();
}

В итоге должно быть два месаджбокса с текстом "Hello" и заголовками "i'm first" и "i'm second".

Хорошо бы найти унивирсальное решение как для stdcall так и для cdecl колбеков. В случае с с stdcall нужно только добавить в ecx указатель this, а в cdecl ещё и перекопировать параметры. Может уже есть готовые решения, что-то не очень горю желанием писать ассемблерные вставки?


--------------------
user posted image
PM Jabber   Вверх
SenkraD
Дата 27.11.2009, 14:42 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Cheloveck,  можно попробовать boost::function (boost::bind):
Код
 void run()
{
     boost::function<void (wchar_t*)> callback; // boost::function имеет оператор приведения к указателю на функцию своей сигнатуры
     if(_id == 1)
     {
          callback = boost::bind(&tester::this_callback, this, _1);
     }
    else
     {
          callback = boost::bind(&tester::this_callback2, this, _1);
      }
     funct(callback);
}


Добавлено через 9 минут и 6 секунд
Звиняюсь, ошибся - нету оператора приведения к поинтеру на функу -> мое решение не верно

Добавлено через 14 минут и 24 секунды
вот нашёл ещё это


--------------------
 Имеющий язык - да не убоится спросить! 
user posted image
PM MAIL ICQ   Вверх
djamshud
Дата 27.11.2009, 15:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Пердупержденный
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 23.11.2009

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



Я бы сделал это так (типы немного поменял, но суть сохранилась):

Закрытая библиотека:

Код

typedef void(callback_t)(char const*);

void callbacker(callback_t func){
printf("Callbacker\n");
func("Inside");
}


Сласс

Код

class Class{
public:
char const *str;
Class(char const*);
void test();
};

Class::Class(char const *str){
this->str=str;
}



Подготовка к колбекам:

Код

Class *global_object;
#define callback_template(func) (global_object=this,func)


Вызов функции с колбеком:

Код

void callback1(char const *str){
printf("1...%s;%s\n",str,global_object->str);
}

void callback2(char const *str){
printf("2...%s;%s\n",str,global_object->str);
}

void Class::test(){
callbacker(callback_template(callback1));
callbacker(callback_template(callback2));
}


Код

int main(){
Class("Class1").test();
Class("Class2").test();
return 0;
}


Результат:

Код

Callbacker
1...Inside;Class1
Callbacker
2...Inside;Class1
Callbacker
1...Inside;Class2
Callbacker
2...Inside;Class2


Это сообщение отредактировал(а) djamshud - 27.11.2009, 15:29


--------------------
'Cuz I never walk away from what I know is right
Alice Cooper - Freedom
PM   Вверх
Cheloveck
Дата 27.11.2009, 15:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

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



djamshud, решение для одного потока более или менее подходит, но при появлении второго всё станет печально.

SenkraD, пока не увидел решения этой проблемы, но я ещё поизучаю эту тему.


--------------------
user posted image
PM Jabber   Вверх
djamshud
  Дата 27.11.2009, 16:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Пердупержденный
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 23.11.2009

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



>решение для одного потока более или менее подходит, но при появлении второго всё станет печально.

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

Если время выполнения функции и колбеков невелико, можно просто закрыть мутеком операции присваивания global_object и вызова функции.  Когда-то я делал именно так. Там еще была рюшечка в виде произвольного типа класса (т.е. не привязывался непосредственно к Class). Если интересует, могу поискать.

В обоих случаях пользоваться будет менее удобно, но в общем терпимо.

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


--------------------
'Cuz I never walk away from what I know is right
Alice Cooper - Freedom
PM   Вверх
null56
Дата 27.11.2009, 16:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Ну если закрыть глаза на треадсейфность... то колбек метода класса можно вызвать так.... (я приведу рабочий свой пример)
Код

// прототип
typedef void (MyObj :: *Command)(const char *);


// присваивает адрес колбека
Command cmd = &MyObj :: first;

// вызов из метода класса
(this->*cmd)("preved 1");


я надеюсь, что я правильно понял вопрос... вызов метода определенного объекта через колбак... внутри метода first я могу пользоваться как данными самого объекта (в данном случае this), так и переданный параметр строку
PM MAIL   Вверх
null56
Дата 27.11.2009, 17:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



иными словами, чтобы вызвать колбек метода, нужен сам объект + адрес на метод... в моем примере я делаю это через this, так как вызываю колбек будучи в метода самого объекта... но тоже самое можно сделать и вне методов объета
PM MAIL   Вверх
djamshud
Дата 27.11.2009, 17:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Пердупержденный
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 23.11.2009

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



Вы неправильно поняли). Посмотрите первое сообщение топика, а точнее первый кусок "Код С++". Указатель на метод туда никак не впихнуть.


--------------------
'Cuz I never walk away from what I know is right
Alice Cooper - Freedom
PM   Вверх
Cheloveck
Дата 27.11.2009, 20:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

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



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

Эта тема всплыла у меня на этот раз из-за SQLite, но к нему я нашёл C++ враппер. Посмотрю ещё как он работает (быстро проглядел - там boost), если найду что-то интересное - отпишусь сюда.

Всем спасибо, но вопрос по ходу не решаемый (во всяком случае без ассемблера).


--------------------
user posted image
PM Jabber   Вверх
Abyx
Дата 27.11.2009, 21:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Cheloveck, при чем тут ассемблер?

PM MAIL   Вверх
Abyx
Дата 27.11.2009, 21:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



если в коллбек передается какое-то значение которое можно использовать как ключ - делать карту объектов, например для оконной функции - map<HWND, YourClass>

если объект 1 на поток, надо использовать TLS, - фактически id потока - ключ для выбора объекта

если в коллбек прямо или косвенно не передается никаких значений, позволяющих выбрать нужный объект, то надо создавать уникальные коллбеки
либо статически, фиксированное количество - сделать карту<функция,объект>
Код

template<class T, int id>
struct Thunk
{
    static 
    R callback(...) // тут - сигнатура коллбека
    {
        return t->callback(...);
    }
    static T* t;
};
Thunk<YourClass,1>::t = &yourObject_1;
Thunk<YourClass,2>::t = &yourObject_2;

SetCallback( &Thunk<YourClass,1>::callback );
SetCallback( &Thunk<YourClass,2>::callback );


либо динамически, но это непереносимо, т.к. асм
PM MAIL   Вверх
Cheloveck
Дата 27.11.2009, 21:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

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



Цитата(Abyx @  27.11.2009,  22:31 Найти цитируемый пост)
например для оконной функции


Цитата(Abyx @  27.11.2009,  22:31 Найти цитируемый пост)
если объект 1 на поток


Цитата(Abyx @  27.11.2009,  22:31 Найти цитируемый пост)
если в коллбек прямо или косвенно не передается никаких значений


Цитата(Abyx @  27.11.2009,  22:31 Найти цитируемый пост)
либо статически


Я говорил об универсальном способе, а таких "если" уже много было...


--------------------
user posted image
PM Jabber   Вверх
Abyx
Дата 27.11.2009, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Cheloveck, тогда тебе нужна универсальная программа %)

Добавлено через 2 минуты и 27 секунд
наиболее универсальный метод - динамически создавать коллбеки,
но это привязывает к архитектуре проца и системному АПИ управления свойствами памяти
PM MAIL   Вверх
Cheloveck
Дата 27.11.2009, 22:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

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



Abyx, ничего универсального. Если выделять память и записывать туда то, что требуется для вызова (я об этом говорил в первом посте) и передавать эту область в функцию вместо колбека - то всё будет замечательно. Так работают оконные колбеки в делфях. Но я не хочу асм 
Цитата(Abyx @  27.11.2009,  22:31 Найти цитируемый пост)
но это непереносимо, т.к. асм 




--------------------
user posted image
PM Jabber   Вверх
mes
Дата 28.11.2009, 00:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


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

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



Цитата(djamshud @  27.11.2009,  14:25 Найти цитируемый пост)
Я бы сделал это так (типы немного поменял, но суть сохранилась):

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

#include <iostream>
#include <list>

void lib_func ( void (*callback)(int)) { callback (12); }

class Lib
{

    public:
        class IListener {
            public:
                virtual ~IListener () {}
                virtual void Event (int) =0;
        };


        static void func ()   { lib_func (&callback); }
        static void reg   (IListener * p) { m_register.push_back(p); }
        static void unreg (IListener * p) {}

    private:
         Lib ();
        ~Lib ();

        static void callback (int i) {
                for ( std::list<IListener*>::iterator it=m_register.begin(); it != m_register.end(); ++it)
                {
                    (*it)->Event(i);
                }
              }
        static std::list<IListener*>  m_register;
};

std::list<Lib::IListener*>  Lib::m_register;


class Tester : public Lib::IListener
{
    public:
        Tester (const std::string& name, int offset =0) : m_name (name), m_offset(offset) {}
        virtual void Event (int i) { std::cout << m_name << " " << i+m_offset << " " << std::endl; }
    private:
        int m_offset;
        std::string m_name;

};

int main ()
{
   Tester t1 ("a"), t2("b",2);
   Lib::reg(&t1);
   Lib::reg(&t2);

   Lib::func ();

   Lib::unreg(&t2);
   Lib::unreg(&t1);

  return 0;
}




писал на скорую руку, чтоб лишь передать суть, ну и поэтому осталось целое поле деятельности для дальнейшего развития.. smile



--------------------
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

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

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

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

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


 




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


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

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