Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Передача указателя метода класса в качестве аргумента функции

590
27 февраля 2008 года
Gigahard
223 / / 03.04.2006
Собственно ранее уже возникала проблема с чем то подобным в STL, но теперь новая заморочка.
Есть код:
Код:
//---------------------------------------------------------------------------
#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;
}
//---------------------------------------------------------------------------

Есть некая внешняя функция которая запускает задачи. Задачи передаются как параметр этой функции. Собой этот параметр представляет указатель на пользовательскую функцию задачи.

Когда мы создаем каждую задачу отдельно и передаем указатель на нее функции стартеру, то все нормально. Но стоит все пользовательские задачи завернуть в класс, сделав их методами класса, как функция стартер начинает ругаться на неверный тип указателя. К примеру в данной ситуации функция стартер должна принимать указатель типа
 
Код:
void (*)(char*)

А задача запихнутая внутрь класса превращается в указатель типа
 
Код:
void (test::*)(char*)

Пробовал танцы с бубном для преобразования указателя к long или через reinterpret_cast<> - не хочет преобразовыватся.

Собственно что делать?
240
27 февраля 2008 года
aks
2.5K / / 14.07.2006
Э как ты себе предстваляешь передачу метода класса в качестве параметра? Она не имеет смысла вне класса, ведь не создаются же отдельно экземпляры исполняемого кода функции для каждого созданного объекта. Она всегда существует в одном экземпляре, но связывается с объектом посредством указателя this. Как метод класса переданный по указателю обращался бы к полям объекта? А если он не работает с полями объекта, то какого черта он делает в классе?
В таком случае передают указатель на сам объект класса или какой то абостракный интерфейс, через который можно вызывть нужные функции.
Передавать по указателю можно только статические методы, как раз потому, что они только они есть в одно экземпляре на весь класс.
276
27 февраля 2008 года
Rebbit
1.1K / / 01.08.2005
Цитата: aks
Передавать по указателю можно только статические методы, как раз потому, что они только они есть в одно экземпляре на весь класс.


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

240
27 февраля 2008 года
aks
2.5K / / 14.07.2006
Ну да, это я и мел ввиду. )
3
27 февраля 2008 года
Green
4.8K / / 20.01.2000
Цитата: Gigahard

Есть некая внешняя функция которая запускает задачи. Задачи передаются как параметр этой функции. Собой этот параметр представляет указатель на пользовательскую функцию задачи.

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

Собственно что делать?



Ну то, что передавать указатель на нестатический метод класса т.о. некорректно, тебе уже объяснили.

А на вопрос "что делать" ответ такой: используй шаблоны и функторы.

Код:
template<typename F>
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;
};
590
27 февраля 2008 года
Gigahard
223 / / 03.04.2006
Цитата:
Она не имеет смысла вне класса, ведь не создаются же отдельно экземпляры исполняемого кода функции для каждого созданного объекта.
Она всегда существует в одном экземпляре, но связывается с объектом посредством указателя this.


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

Но загвоздка совсем не в том чтоб получить указатель на этот метод, а в том, чтобы его привести к виду удобоваримому для функции, которая получает указатель метода в качестве параметра.


Green
Функция DoJob внешняя. Я не могу ее изменять. Так что функторы здесь не совсем в тему...


P.S. Кстати проблема решилась. Достаточно было явно объявить внутриклассовый метод как static. Вообще странно. Ведь он и так должен являтся общим для всех экземпляров этого класса, т.е. и так неявно должен быть статическим...

276
27 февраля 2008 года
Rebbit
1.1K / / 01.08.2005
Цитата: Gigahard
Но загвоздка совсем не в том чтоб получить указатель на этот метод, а в том, чтобы его привести к виду удобоваримому для функции, которая получает указатель метода в качестве параметра.


Ехали мы ехали............
Еще раз. В нестатических методах класа можно использовать нестатические поля етого класа. this->myField. Откуда в методе берется етот волшебный this ? Я в тонкостях С++ баран, но как не крути етот this передается методу как неявный параметр. Тоесть

 
Код:
class MyClass {
  public void myMethod();
}
Реально будет чтото типа
 
Код:
public void myMethod(MyClass* this);
А
 
Код:
MyClass *obj = new MyClass();
obj->myMethod();
премерно
 
Код:
MyClass *obj = new MyClass();
myMethod(obj);
Отсюда предпологаю (потому что не знаю ибо занимаюсь совсем другим) что указатель на метод класа невозможно привести к указателю на фуекцию. Если не прав поправте.

Цитата: Gigahard

Вообще странно. Ведь он и так должен являтся общим для всех экземпляров этого класса, т.е. и так неявно должен быть статическим...


Метод всегда общий. Вопрос передавать ли ему this. То что ты не обрачаешся к полям класа в нестатическом методе еще не значит что this ему не передастся.

590
27 февраля 2008 года
Gigahard
223 / / 03.04.2006
Ну а что мешает при вызове метода класса вне класса, создать временный объект и передать this как неявный параметр?

Т.е. вызов
 
Код:
MyClass::myMethod()

Будет равносилен
 
Код:
MyClass tempObj;
tempObj->myMethod();
или
myMethod(tempObj);

?

Формально можно сказать, что статический метод класса существует вне объекта класса, а не статический метод может существовать только в рамках экземпляра класса. Т.е. доступ к нему может быть открыт только после создания объекта.
Про неявную передачу this, я в начале не подумал... Ведь метод класса должен иметь возможность работы с его внутренними членами, даже если таковые в методе и не используются...
Придется как то крутится со статическим методами и их особенностями...
276
27 февраля 2008 года
Rebbit
1.1K / / 01.08.2005
Про С++ не знаю. Наверно так нельзя потому что все ети преобразования делает компилятор и тебя туда не пустит.
А вот в C# или Java с помощю рефлексии так сделать можно.
То что я написал в предыдущем посте - ето не робий код, просто для понимания сути дела.
590
27 февраля 2008 года
Gigahard
223 / / 03.04.2006
Да я понял, что это описание логики работы компилятора. И отписался в том же духе.;)

В общем спасибо всем за помощь, тонкости осознал, буду думать, что делать дальше... :)
240
27 февраля 2008 года
aks
2.5K / / 14.07.2006
Цитата: Gigahard

P.S. Кстати проблема решилась. Достаточно было явно объявить внутриклассовый метод как static. Вообще странно. Ведь он и так должен являтся общим для всех экземпляров этого класса, т.е. и так неявно должен быть статическим...


Ну вобщето по умолчанию метод является методом объекта, и соотвственно работает с полями конкретного объекта этого класса. Статическим методом именно класса он будет если специально на это указать. Странно было бы обратное. )

276
27 февраля 2008 года
Rebbit
1.1K / / 01.08.2005
Цитата: aks
Статическим методом именно класса он будет если специально на это указать. Странно было бы обратное. )


Ето был бы тихий ужас.
Припустим компилятор может определить есть ли внутри метода обращение к полям или методам объекта (подобний анализ уже есть). Припустим если обращений нет то по дефолту делает метод статическим.
И что мы получаем. Мы делаем метод концептуально нестатический, но в нем не обращаемся к екземпляру обекта (например некая заглушка). Дальше используем етот метод в других методах, по ошибке программиста даже в концептуально статических. Компилятор нам не сообщит об ошибке, поскольку ее то и нет. А потом гдето в глубокок методе делаем обращение к полю и все рухнуло. Правильно что компилятор заставляет указывать слово static поскольку статические методы можно вызывать там где простые нельзя. Нужно явно указывать такую привилегию.

590
27 февраля 2008 года
Gigahard
223 / / 03.04.2006
То есть про передачу внутренних методов класса в функцию DoJob, можно забыть? Даже если я вызываю DoJob внутри класса и внутри класса же передаю указатель на метод?
Получается, что размещая задачу внутри класса, я:
а) Изменяю список параметров, добавляя к списку аргументов неявный параметр this
б) В следствии этого изменяю тип указателя
в) Корректное приведение типа указателя становится невозможным из за разного числа аргументов функции.
в) Передавая адрес задачи во внешнюю функцию, передавая управление этой функции выхожу из видимости указателя this.

В случае объявления метода класса как static, я теряю возможность доступа к нестатическим членам класса...

Ужась... И как быть? Решения нет?
276
27 февраля 2008 года
Rebbit
1.1K / / 01.08.2005
Цитата: Gigahard
Ужась... И как быть? Решения нет?


:) Опять приехали. Я то думал что все гуд и мы уже обсуждаем детали решенной проблемы. Внимательно читай самый первый ответ от aksа и внимательно смотри на пост Greenа.

590
27 февраля 2008 года
Gigahard
223 / / 03.04.2006
Дык делал я уже с функторами... Так... Есть идея :)...

...
:(
...
Два варианта с функторами. Не пашет ни один, в т.ч. и в случае приведения типов:
Код:
//---------------------------------------------------------------------------
#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 нет возможности. Т.е. надо как то впихнуть указатель на функцию в то что имеется. Причём передаваемая функция должна иметь доступ к внутренним не статическим членам класса и в идеале сама быть членом класса.
Как это реализовать, я уже просто не представляю :(...
240
27 февраля 2008 года
aks
2.5K / / 14.07.2006
А зачем ей иметь доступ к нестатическим данным класса? Немного странная постановка задачи.
276
28 февраля 2008 года
Rebbit
1.1K / / 01.08.2005
Ну с функторами не роботал.
Я бы сделал примерно так как сказал aks. Передаем не указатель на функцию, а указатель на интерфейс. В интерфейсе есть метод (тот на который ты хочеш передавать указатель).
Будеш запускать не переданую параметром функцию, а метод переданого обекта реализирующего интерфейс.
3
28 февраля 2008 года
Green
4.8K / / 20.01.2000
Цитата: Gigahard
Дык делал я уже с функторами... Так... Есть идея :)...

...
:(
...
Два варианта с функторами. Не пашет ни один, в т.ч. и в случае приведения типов:


М-да... нет слов...

Цитата: Gigahard

Переписать функцию DoJob нет возможности. Т.е. надо как то впихнуть указатель на функцию в то что имеется. Причём передаваемая функция должна иметь доступ к внутренним не статическим членам класса и в идеале сама быть членом класса.
Как это реализовать, я уже просто не представляю :(...


Если переписать DoJob нет возможности, то может, её вовсе не использовать? Для чего она тебе?

Если оставлять эту функцию, то задаемся другим вопросом: а для чего тебе передавать объект (метод объекта) ? Почему бы не использовать обычную функцию или статических метод, как это предполагалось при создании функции DoJob ?

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

Код:
class Job
{
public:
    void foo();

    static void thunk(char* src) {
        Job* obj = (Job*)src;
        obj->foo();
    }
};

Job job;
DoJob(&Job::thunk, (char*)&job);

Только я бы не советовал злоупотреблять этим методом.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог