Russian Qt Forum
Ноябрь 23, 2024, 08:42 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Об указателях на функции класса  (Прочитано 8959 раз)
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« : Апрель 21, 2009, 22:16 »

Всем привет!

Предположим у меня есть некоторая функция, аргументом которой является указатель на функцию:

Код
C++ (Qt)
typedef int (*pFunc)(int);
int myFunction(pFunc pf){...}
...
 

Далее, мне захотелось написать некоторый класс, например такой:

Код
C++ (Qt)
class MyClass
{
public:
int function(int x) {return x;} // for example
};
 
   

А теперь, как мне передать в функцию myFunction(pFunc pf) указатель на функцию данного класса (function(int)) ?

ну первое что приходит на ум это примерно так:
Код
C++ (Qt)
#include <iostream>
using namespace std;
 
typedef int (*pFunc)(int);
 
class SomeClass
{
public:
int f(pFunc pf) {return (*pf)(2);} // for example
};
 
class MyClass
{
public:
int function(int x) {return x;} // for example
};
 
int main()
{
SomeClass sc;
int x = sc.f(reinterpret_cast<pFunc>(&MyClass::function));
 
cout << x << endl;
return 0;
}
 

Но компилятор выдаёт ошибку:
Код:
ошибка: преобразование из ‘int (MyClass::*)(int)’ в ‘int (*)(int)’

Есть ли выход, господа?  Непонимающий


« Последнее редактирование: Апрель 21, 2009, 22:29 от shapoclak » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Rcus
Гость
« Ответ #1 : Апрель 21, 2009, 22:25 »

Если не менять сигнатуру, то boost::bind?
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #2 : Апрель 21, 2009, 23:09 »

Спасибо! Пойду раскуривать boost::bind  Улыбающийся

P.S. Я конечно уважаю библиотеку Boost, но всё же: есть ли ещё решения ?
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Barmaglodd
Гость
« Ответ #3 : Апрель 22, 2009, 07:22 »

Сделать абстрактный класс
Код:
class IBase
{
public:
        virtual ~IBase() {}
virtual int function(int x)=0;
};
и спользовать его вместо указателя на функцию, иначе boost::bind и boost::function.
Записан
Rcus
Гость
« Ответ #4 : Апрель 22, 2009, 07:39 »

Ну я же сказал что если не менять сигнатуру функции, иначе можно просто
Код:
typedef ReturnType (ClassName::*MethodPtrType)(ParamType1, ParamType2);
Записан
Barmaglodd
Гость
« Ответ #5 : Апрель 22, 2009, 10:22 »

Сигнатуру какой функции? Boost::bind или Boost::function умеют приводиться к указателю на функцию? Без изменения int myFunction(pFunc pf){...} разве можно что-то сделать?
Записан
Tonal
Гость
« Ответ #6 : Апрель 22, 2009, 10:23 »

Если не менять сигнатуру, то только через статическую функцию и запомненный экземпляр.
Никакой boost::bind тут не поможет, т. к. он отдаёт функтор а не указатель на функцию.
Если же сигнатуру можно менять, то вариантов куча. Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #7 : Апрель 22, 2009, 20:09 »

Всем спасибо за обсуждение  Улыбающийся

Ясно что указатели на члены классов ссылаются на область памяти внутри класса, а члены класса определяются смещением. Реальный адрес получается в результате применения смещения к начальному адресу конкретного объекта...
Мне лично хотелось узнать есть ли возможность приведения типа:
Код
C++ (Qt)
pFunc pf = (pFunc)&MyClass::function;
 
 
Ну похоже, что нет... Может это и правильно  Улыбающийся

Ладно, пойду пивка попью  Смеющийся Ещё раз всем спасибо! 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Alex03
Гость
« Ответ #8 : Апрель 23, 2009, 08:07 »

Всем спасибо за обсуждение  Улыбающийся

Ясно что указатели на члены классов ссылаются на область памяти внутри класса, а члены класса определяются смещением. Реальный адрес получается в результате применения смещения к начальному адресу конкретного объекта...
Мне лично хотелось узнать есть ли возможность приведения типа:
Код
C++ (Qt)
pFunc pf = (pFunc)&MyClass::function;
 
В каждый не статический метод класса неявно передаётся ещё один параметр - указатель на объект данного класса (тот самый this).

shapoclak Вы лучше расскажите что вам надо в результате, а народ подскажет как красивее это сделать.
Записан
vaprele07
Гость
« Ответ #9 : Апрель 23, 2009, 10:38 »

другое дело как этот "this" представляют компиляторы с разной оптимизацией и т.д.
короче так делать не хорошо. проще как выше сказали сделать наследование от IBase какая разница что хранить указатель на функцию или указатель на класс если все равно придется про "this" думать!
Записан
Eugene Efremov
Гость
« Ответ #10 : Апрель 26, 2009, 22:37 »

Мне лично хотелось узнать есть ли возможность приведения типа:
Код
C++ (Qt)
pFunc pf = (pFunc)&MyClass::function;
 
 
Ну похоже, что нет... Может это и правильно  Улыбающийся

Даже если бы и была — в результате получился не pFunc, а что-то вроде int (*)(const MyClass*, int). Но на деле — указатели на члены очень сильно отличаются по своему устройству от обычных. Так что никакое подобное приведение невозможно. В принципе, возможно это обойти, но в любом случае — чтобы получить требуемый int (*)(int) придется где-то хранить указатель на объект. С учетом этого получаем примерно такой код:

Код
C++ (Qt)
template<class type, int (type::*fun)(int)>
class StFunc
{
static type *owner;
static int call(int i)
{
return (owner->*fun)(i);
}
public:
static pFunc get_ptr(type *t)
{
owner=t;
return &StFunc<type, fun>::call;
}
};
 
template<class type, int (type::*fun)(int)> type* StFunc<type, fun>::owner;
 

Юзать это дело так:

Код
C++ (Qt)
MyClass m;
pFunc pm = StFunc<MyClass, &MyClass::function>::get_ptr(&m);
 

Главная проблема в том, что и ф-ция call — указатель на которую мы передаем вместо требуемого — должна быть статической (чтобы это был обычный указатель на ф-цию). А значит — указатель на объект MyClass, который она использует, тоже должен быть статическим.
Это в свою очередь означает, что одновременно мы сможем использовать только один такой указатель для каждой пары класс/метод.

Иными словами — это костыль. Очень кривой и с большим количеством потенциальных граблей. Так что единственный случай, когда такое нужно использовать — это если мы не можем никак изменить ф-цию, которая этот самый pFunc хочет. Если же мы ее можем изменить, можно сделать гораздо проще:

Было:
Код
C++ (Qt)
typedef int (*pFunc)(int);
 
int myFunction(pFunc pf)
{
int i;
// do something...
return pf(i);
}
 

Стало:
Код
C++ (Qt)
template<class TFunc>
int myFunction(TFunc pf)
{
int i;
// do something...
return pf(i);
}
 

И все. Теперь мы можем передавать туда не только указатели на ф-ции, но и вообще все, что позволяет применить к нему operator().

Правда, напрямую мы передавать туда указатели на члены все еще не можем: для их вызова все еще требуется указатель на сам объект. А значит — если мы не хотим писать версию myFunction с двумя аргументами — нам все еще нужен переходник. Но теперь он будет устроен гораздо проще, а главное — мы избавились от статики, а значит и от ограничений на одновременное использование:
Код
C++ (Qt)
template<class type>
class MemFun
{
type *owner;
int (type::*fun)(int);
public:
MemFun(type *t, int (type::*f)(int)) : owner(t), fun(f) {}
 
int operator()(int i)
{
return (owner->*fun)(i);
}
};
 
............
// using:
 
MyClass m;
MemFun<MyClass> pm(&m, &MyClass::function);
myFunction(pm);
 

А теперь можно вспомнить, что подобный функционал (причем гораздо более гибкий, чем наш самопальный MemFun) давно уже реализован во всяких Boost и иже с ними. Собственно, именно его и предлагали использовать выше по треду...
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #11 : Апрель 27, 2009, 18:00 »

Eugene Efremov,
Спасибо за исчерпывающий обзор!

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

На самом деле на функцию которая получает указатель не накладывается такого жёсткого ограничения, поэтому вариантов здесь не мало, более того, заранее известно даже то, что использовать она будет функцию известного класса и даже известно какую  Улыбающийся Поэтому самый простой вариант это такой:
Код
C++ (Qt)
int myFunction(MyClass *myClass)
{
   int i;
   ...
   return myClass->function(i);
}
 

Но пересмотрев архитектуру программы, наверно самое логичное решение унаследоваться от абстрактного класса, как предложил Barmaglodd ну и кое что ещё по мелочи изменить...




« Последнее редактирование: Апрель 27, 2009, 18:04 от shapoclak » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.28 секунд. Запросов: 23.