Передача указателя метода класса в качестве аргумента функции
Есть код:
#include <stdio>
#include <windows>
void DoJob(void(*job)(char*), char* source)//Функция определенная где то "на стороне", к примеру подгружаемая из DLL
{
job(source);
}
//----------------------------------------
void JOB_1(char* src) //Внешняя пользовательская задача 1
{
printf("%s\n", src);
}
//----------------------------------------
void JOB_2(char* src) //Внешняя пользовательская задача 2
{
printf("%s\n", CharUpper(src));
}
class test
{
public:
test(char* src):Source(src){}
~test(){}
char* Source;
void InternalProc(void(*job)(char*)) //Некая внутренняя функция класса, вызывающая в процессе работы внешнюю DoJob
{
DoJob(job, Source);
//... какой то код...
}
void JOB_3(char* src) //Пользовательская задача, определенная только для этого класса
{
printf("%s\n", src);
}
void JOB_4(char* src) //Еще одна пользовательская задача
{
printf("%s\n", CharUpper(src));
}
};
int main(int argc, char* argv[])
{
//Вызов функций на прямую, без класса обертки.
DoJob(JOB_1, "gigi");
DoJob(JOB_2, "gigi");
//----- А теперь применение задач определенных внутри класса -----
test a("Bua ga ga :)");
a.InternalProc(JOB_1); //Внешние задачи вызываются без проблем
//a.InternalProc(test::JOB_4); //Внутренние задачи не вызываются напрямую из класса. Компилятор ругается на несоответствие типа указателя на функцию
return 0;
}
//---------------------------------------------------------------------------
Есть некая внешняя функция которая запускает задачи. Задачи передаются как параметр этой функции. Собой этот параметр представляет указатель на пользовательскую функцию задачи.
Когда мы создаем каждую задачу отдельно и передаем указатель на нее функции стартеру, то все нормально. Но стоит все пользовательские задачи завернуть в класс, сделав их методами класса, как функция стартер начинает ругаться на неверный тип указателя. К примеру в данной ситуации функция стартер должна принимать указатель типа
А задача запихнутая внутрь класса превращается в указатель типа
Пробовал танцы с бубном для преобразования указателя к long или через reinterpret_cast<> - не хочет преобразовыватся.
Собственно что делать?
В таком случае передают указатель на сам объект класса или какой то абостракный интерфейс, через который можно вызывть нужные функции.
Передавать по указателю можно только статические методы, как раз потому, что они только они есть в одно экземпляре на весь класс.
Если быть точнее, то потому что им не передается неявный параметр инстанса объекта для которого ети методы вызываются.
То что нестатические методы тоже в одном екземпляре ты сам сказал выше :).
Есть некая внешняя функция которая запускает задачи. Задачи передаются как параметр этой функции. Собой этот параметр представляет указатель на пользовательскую функцию задачи.
Когда мы создаем каждую задачу отдельно и передаем указатель на нее функции стартеру, то все нормально. Но стоит все пользовательские задачи завернуть в класс, сделав их методами класса, как функция стартер начинает ругаться на неверный тип указателя.
Собственно что делать?
Ну то, что передавать указатель на нестатический метод класса т.о. некорректно, тебе уже объяснили.
А на вопрос "что делать" ответ такой: используй шаблоны и функторы.
void DoJob(F& job, const char* source)
{
job(source);
}
class SomeFunctor
{
public:
SomeFunctor(const char* src): source(src){}
~SomeFunctor(){}
operator()(const char* src)
{
printf("%s\n", CharUpper(src));
}
private:
const char* source;
};
Она всегда существует в одном экземпляре, но связывается с объектом посредством указателя this.
Да я как бы с этим и не спорю и не нуждаюсь в персональном адресе метода для каждого экземпляра класса. Мне этот метод как раз интересен в виде единственного экземпляра так сказать статической функции.
Но разве методы класса по умолчанию не являются общими для всех объектов этого класса? Т.е. статическими?
Но загвоздка совсем не в том чтоб получить указатель на этот метод, а в том, чтобы его привести к виду удобоваримому для функции, которая получает указатель метода в качестве параметра.
Green
Функция DoJob внешняя. Я не могу ее изменять. Так что функторы здесь не совсем в тему...
P.S. Кстати проблема решилась. Достаточно было явно объявить внутриклассовый метод как static. Вообще странно. Ведь он и так должен являтся общим для всех экземпляров этого класса, т.е. и так неявно должен быть статическим...
Ехали мы ехали............
Еще раз. В нестатических методах класа можно использовать нестатические поля етого класа. this->myField. Откуда в методе берется етот волшебный this ? Я в тонкостях С++ баран, но как не крути етот this передается методу как неявный параметр. Тоесть
public void myMethod();
}
obj->myMethod();
myMethod(obj);
Вообще странно. Ведь он и так должен являтся общим для всех экземпляров этого класса, т.е. и так неявно должен быть статическим...
Метод всегда общий. Вопрос передавать ли ему this. То что ты не обрачаешся к полям класа в нестатическом методе еще не значит что this ему не передастся.
Т.е. вызов
Будет равносилен
tempObj->myMethod();
или
myMethod(tempObj);
?
Формально можно сказать, что статический метод класса существует вне объекта класса, а не статический метод может существовать только в рамках экземпляра класса. Т.е. доступ к нему может быть открыт только после создания объекта.
Про неявную передачу this, я в начале не подумал... Ведь метод класса должен иметь возможность работы с его внутренними членами, даже если таковые в методе и не используются...
Придется как то крутится со статическим методами и их особенностями...
А вот в C# или Java с помощю рефлексии так сделать можно.
То что я написал в предыдущем посте - ето не робий код, просто для понимания сути дела.
В общем спасибо всем за помощь, тонкости осознал, буду думать, что делать дальше... :)
P.S. Кстати проблема решилась. Достаточно было явно объявить внутриклассовый метод как static. Вообще странно. Ведь он и так должен являтся общим для всех экземпляров этого класса, т.е. и так неявно должен быть статическим...
Ну вобщето по умолчанию метод является методом объекта, и соотвственно работает с полями конкретного объекта этого класса. Статическим методом именно класса он будет если специально на это указать. Странно было бы обратное. )
Ето был бы тихий ужас.
Припустим компилятор может определить есть ли внутри метода обращение к полям или методам объекта (подобний анализ уже есть). Припустим если обращений нет то по дефолту делает метод статическим.
И что мы получаем. Мы делаем метод концептуально нестатический, но в нем не обращаемся к екземпляру обекта (например некая заглушка). Дальше используем етот метод в других методах, по ошибке программиста даже в концептуально статических. Компилятор нам не сообщит об ошибке, поскольку ее то и нет. А потом гдето в глубокок методе делаем обращение к полю и все рухнуло. Правильно что компилятор заставляет указывать слово static поскольку статические методы можно вызывать там где простые нельзя. Нужно явно указывать такую привилегию.
Получается, что размещая задачу внутри класса, я:
а) Изменяю список параметров, добавляя к списку аргументов неявный параметр this
б) В следствии этого изменяю тип указателя
в) Корректное приведение типа указателя становится невозможным из за разного числа аргументов функции.
в) Передавая адрес задачи во внешнюю функцию, передавая управление этой функции выхожу из видимости указателя this.
В случае объявления метода класса как static, я теряю возможность доступа к нестатическим членам класса...
Ужась... И как быть? Решения нет?
:) Опять приехали. Я то думал что все гуд и мы уже обсуждаем детали решенной проблемы. Внимательно читай самый первый ответ от aksа и внимательно смотри на пост Greenа.
...
:(
...
Два варианта с функторами. Не пашет ни один, в т.ч. и в случае приведения типов:
#include <stdio>
#include <windows>
typedef void (*JOBPTR)(char*);
void DoJob(void(*job)(char*), char* source)//Функция определенная где то "на стороне", к примеру подгружаемая из DLL
{
job(source);
}
class test;
class JOB_1
{
public:
JOB_1(test* lnk):link(lnk){}
~JOB_1(){}
void operator()(char* src)
{
printf("%s\n", src);
}
test* link;
};
class test
{
public:
test(char* src):Source(src){}
~test(){}
char* Source;
void InternalProc()
{
DoJob(&JOB_2(), Source);
}
void ExternalProc()
{
DoJob(&JOB_1(this), Source);
}
class JOB_2
{
public:
JOB_2(){}
~JOB_2(){}
void operator()(char* src)
{
printf("%s\n", src);
}
};
};
int main(int argc, char* argv[])
{
test a("Bua ga ga :)");
a.ExternalProc();
a.InternalProc();
return 0;
}
//---------------------------------------------------------------------------
Переписать функцию DoJob нет возможности. Т.е. надо как то впихнуть указатель на функцию в то что имеется. Причём передаваемая функция должна иметь доступ к внутренним не статическим членам класса и в идеале сама быть членом класса.
Как это реализовать, я уже просто не представляю :(...
Я бы сделал примерно так как сказал aks. Передаем не указатель на функцию, а указатель на интерфейс. В интерфейсе есть метод (тот на который ты хочеш передавать указатель).
Будеш запускать не переданую параметром функцию, а метод переданого обекта реализирующего интерфейс.
...
:(
...
Два варианта с функторами. Не пашет ни один, в т.ч. и в случае приведения типов:
М-да... нет слов...
Переписать функцию DoJob нет возможности. Т.е. надо как то впихнуть указатель на функцию в то что имеется. Причём передаваемая функция должна иметь доступ к внутренним не статическим членам класса и в идеале сама быть членом класса.
Как это реализовать, я уже просто не представляю :(...
Если переписать DoJob нет возможности, то может, её вовсе не использовать? Для чего она тебе?
Если оставлять эту функцию, то задаемся другим вопросом: а для чего тебе передавать объект (метод объекта) ? Почему бы не использовать обычную функцию или статических метод, как это предполагалось при создании функции DoJob ?
Если ответив на эти вопросы ты все равно уверен в необходимости передачи объекта во внутрь DoJob, то готовься к хаку - самому крайнему и некрасивому поступку.
Передавай объект вторым параметром, т.е. параметром source, а в качестве первого параметра передавай функцию (стат.метод) вызывающий метод этого объекта:
{
public:
void foo();
static void thunk(char* src) {
Job* obj = (Job*)src;
obj->foo();
}
};
Job job;
DoJob(&Job::thunk, (char*)&job);
Только я бы не советовал злоупотреблять этим методом.