Здравствуйте, уважаемые.
Столкнулся со следующей проблемой: есть функция, один из параметров которой является указателем на функцию, например
int F(FUNCPOINTER funcpointer);
Тип FUNCPOINTER определен так:
typedef int (*FUNCPOINTER)();
Проблема состоит в том, что в функцию F надо передавать адрес нестатического метода некоторого класса. Компилятор при попытке вызвать F(object.MethodName) ругается. Если передать адрес обычной функции — то проблем нет. Если метод статический — тоже работает. Но для нестатического метода — нет. Подскажите пожалуйста, как можно решить эту проблему. Использую компилятор VC++ 6.0
Заранее благодарен,
mailto:dimon-main@mail.ru
Здравствуйте, Master-Win, Вы писали:
MW>Здравствуйте, уважаемые.
MW>Столкнулся со следующей проблемой: есть функция, один из параметров которой является указателем на функцию, например
MW>int F(FUNCPOINTER funcpointer);
MW>Тип FUNCPOINTER определен так:
MW>typedef int (*FUNCPOINTER)();
MW>Проблема состоит в том, что в функцию F надо передавать адрес нестатического метода некоторого класса. Компилятор при попытке вызвать F(object.MethodName) ругается. Если передать адрес обычной функции — то проблем нет. Если метод статический — тоже работает. Но для нестатического метода — нет. Подскажите пожалуйста, как можно решить эту проблему. Использую компилятор VC++ 6.0
F(&ClassName::MethodName)
MW>Заранее благодарен, mailto:dimon-main@mail.ru
MW>Проблема состоит в том, что в функцию F надо передавать адрес нестатического метода некоторого класса.
Я вот чего придумал:
#include <iostream>
typedef int (*fptr)();
struct B {
int i;
int pr() { std::cout << "B()" << i << std::endl;}
};
template <B* T> int func_wrap() {
T->pr();
return 0;
}
int f(fptr ptr) {
ptr();
return 0;
}
B b; //увы, только глобальная переменная, что фактически обессмысливает всю идею, но так, в академических целях...
B b1;
int main() {
b1.i = 10;
b.i = 20;
f(func_wrap<&b>);
f(func_wrap<&b1>);
}
Здравствуйте, Master-Win, Вы писали:
MW>Проблема состоит в том, что в функцию F надо передавать адрес нестатического метода некоторого класса. Компилятор при попытке вызвать F(object.MethodName) ругается. Если передать адрес обычной функции — то проблем нет. Если метод статический — тоже работает. Но для нестатического метода — нет. Подскажите пожалуйста, как можно решить эту проблему.
С такой сигнатурой — ничего у тебя не выйдет, кроме хака с ассемблерными переходниками.
Функция-член принимает скрытый параметр (указатель на объект). А сигнатура твоего колбека такое не предусматривает.
(Обычно у колбек-функций заводят пользовательский параметр — int(*MY_CALLBACK_FUNC)(
void*) или что-то в таком роде).
Причём указатель на функцию-член ещё должен уметь выполнять кастинг и вызов виртуальных функций — поэтому он несовместим по формату с указателями на обычные функции (т.е. нельзя на халяву сделать reinterpret_cast).
И передавать туда this как обычный параметр тоже нельзя — у функций-членов своя конвенция вызова — __thiscall — которая определяет наилучший способ передачи this (на стеке, в регистре, и т.п.)
MW> Использую компилятор VC++ 6.0
Компилятор, конечно, дохлый, ну да ничего страшного.
WTL вовсю пользуется ассемблерными переходниками.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Доброго времени суток...
Сходная тема была рассмотрена в
http://rsdn.ru/Forum/Info/FAQ.cpp.threadandmember.aspxАвтор: MaximE
Дата: 08.08.04
Буду рад, если поможет...
Здравствуйте, Master-Win, Вы писали:
Вот небольшой ликбез по теме
Указатели на функции и указатели на методы
Дело в том, что указатель на метод существенно отличается по типу от обычного указателя на функцию, даже если прототипы функции и метода внешне идентичны. Вспомним, как определяется указатель на функцию
тип (*имя-указателя)(спецификация параметров);
Для сокращения записи применяют обычно оператор typedef. Объявление
typedef тип (*тип-указателя)(спецификация параметров);
вводит новое имя типа. После этого указатель на функцию можно объявлять как
тип-указателя имя-указателя;
Указателю присваивается адрес функции. В С++ разрешается присваивать адрес функции либо явно задавая операцию получения адреса,
имя-указателя = &имя-функции;
либо неявно
имя-указателя = имя-функции;
В последнем случае имя функции неявно преобразуется в адрес.
ПРИМЕЧАНИЕ
В системе Visual C++.NET 2003 для библиотечных функций работает только вариант с явной операцией адресации. Второй вариант вызвал фатальную ошибку компилятора fatal error 1001: INTERNAL COMPILER ERROR. Borland C++ Builder 6 и тот, и другой вариант обрабатывает правильно.
Вызов функции через указатель выполняется опять же либо так:
(*имя-указателя)(аргументы);
либо в более простой форме,
имя-указателя(аргументы);
которая эквивалентна предыдущей
Рассмотрим несколько простых примеров. Допустим, у нас объявлен указатель на функцию
int (*pf)(void);
Этому указателю можно присвоить адрес любой функции с таким же прототипом, в том числе и библиотечной. Например, такой прототип имеет функция-генератор случайного числа rand(), который прописан в заголовке библиотеки <cstdlib>.
int f1(void) { return 1; }
extern “C” int f2(void) { return 2; }
pf = f1; cout << pf() << endl; // вызов f1()
pf = f2; cout << pf() << endl; // вызов f2()
pf = &rand; cout << pf() << endl; // вызов rand()
Как видите, спецификация компоновки никак не мешает косвенному вызову. Напишем теперь простой класс
class Constant
{ int value;
public:
Constant(const int &a): value(a) {}
int get(void) { return value; }
};
Метод get() с виду имеет тот же прототип, что и функции в приведенном выше фрагменте. Однако попытки присвоить адрес метода get() указателю pf компилятор пресекает «на корню». Да и не совсем понятно, как этот адрес задавать. Вариантов два:
— объявить объект и взять адрес метода в объекте;
— не объявлять объект, а приписать префикс класса.
// первый вариант
Constant A(5); pf = &A.get();
// второй вариант
pf = &Constant::get;
Первый вариант вообще неверный: и Visual C++.NET 2003, и Borland C++ Builder 6 сообщают, что операцию взятия адреса & так использовать нельзя. Второй вариант – ближе к истине: оба компилятора сообщают только о невозможности выполнить преобразование типов. Попытки прописать преобразование явно — не проходят.
pf = static_cast<int (*)(void)>(&Constant::get);
pf = reinterpret_cast<int (*)(void)>(&Constant::get);
pf = (int (*)(void))(&Constant::get);
Все эти варианты вызывают ту же ошибку компиляции — невозможность преобразования типов. Таким образом, тип указателя на метод класса кардинально отличается от типа указателя на функцию: адрес метода нельзя присвоить указателю на функцию, даже если внешне их прототипы совпадают. Это становится понятным, если мы вспомним, что нестатические методы получают дополнительный параметр — указатель this.
Аналогично, нельзя присвоить обычному указателю на функцию адрес виртуального метода — в этом случае дело усугубляется еще наличием в составе объекта указателя на таблицу виртуальных методов. А вот со статическими методами картина другая! Статический метод не получает никаких «лишних параметров», поэтому его адрес можно присваивать обычному указателю на функцию без всяких преобразований. Добавим в класс Constant статическое поле и статический метод с нужным нам прототипом:
class Constant
{ int value;
static int d;
public:
Constant(const int &a): value(a) {}
int get(void) { return value; }
static int getd(void) { return d; } // статический метод
};
Тогда нашему указателю pf можно присвоить адрес статического метода вторым из приведенных выше способов, например:
pf = Constant::getd; // или pf = &Constant::getd;
Префикс, естественно, необходимо писать.
Указатель на метод объявляется по-другому [1-8.3.3] — нужно задать префикс-имя класса:
int (Constant::*pm)(void);
Такому указателю можно присваивать адреса обычных и виртуальных методов вторым их показанных выше способов, например
pm = &Constant::get; // или pm = Constant::get;
Адрес статического метода, так же как и адрес обычной функции, такому указателю присвоить нельзя — возникает ошибка трансляции: компилятор сообщает о невозможности выполнить преобразование типов.
И косвенный вызов метода выполняется по-другому — с помощью операции выбора члена класса .* или ->* [1-5.5]. Несмотря на то, что указатель на член класса является отдельной, независимой от класса переменной, вызов метода по указателю возможен только при наличии объекта, например
Constant A(5);
cout << (A.*pm)() << endl;
Выражение(A.*pm)() означает следующее: для объекта A вызвать метод, чей адрес записан в указателе pm. Слева от операции .* — объект, справа — указатель на метод. Скобки вокруг выражения A.*pm писать обязательно, так как приоритет операции () вызова функции выше, чем приоритет операции выбора члена класса .*.
В выражении (объект.*указатель) можно заменить часть «объект.» на «указатель->», например
Constant *pc = new Constant(7);
cout << (pc->*pm)() << endl;
Обратите внимание: в выражении (pc->*pm) слева — обычный указатель на динамический объект, а справа — указатель на метод этого объекта. Это совершенно два разных типа указателя.
Интересно, что в системе Visual C++.NET 2003 размер sizeof(pf) указателя на функцию совпадает с размером sizeof(pm) указателя на член класса и равен 4 байта. А вот система Borland C++ Builder 6 выдает совершенно разные цифры: размер указателя на функцию равен 4 байтам, а размер указателя на метод — 12 байт!