Приведение указателя на функцию-член к обычному указателю

В настоящее время у меня есть класс такого типа, сокращенный для простоты:

class MyClass {
    public: 
        MyClass();
        void* someFunc(void* param);
}

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

void secondFunc(int a, int b, void *(*pCallback)(void*));

Теперь мне нужно передать адрес someFunc экземпляра.

Нерабочий образец:

MyClass demoInstance;
// some other calls
secondFunc( 1, 2, demoInstance::someFunc() );

Я пробовал также с такими бросками, как:

(void* (*)(void*)) demoInstance::someFunc;
reinterpret_cast<(void* (*)(void*))>(demoInstance::someFunc);

Как я могу вызвать эту функцию с функцией-членом класса в качестве параметра, чтобы этот мог использовать ее как обратный вызов?

Любая идея или замечание приветствуется. Спасибо и с уважением Тобиас


person Atmocreations    schedule 18.11.2010    source источник
comment
Вы должны предоставить экземпляр; в противном случае это неопределенное поведение. Что вы пытаетесь достичь.   -  person strager    schedule 18.11.2010
comment
Какой указатель передается в функцию обратного вызова в качестве ее параметра? Если вы можете указать это как-то, вы сможете создать struct Foo { MyClass *a; void *b; };, а затем использовать void *MyCallback(void *foo_) { Foo *foo = static_cast<Foo*>(foo_); return foo->a->someFunc(foo->b); } в качестве обратного вызова.   -  person Steve Jessop    schedule 18.11.2010


Ответы (6)


Разница между функцией C и функцией-членом C++ заключается в том, что функция C использует соглашение о вызовах cdecl, а функции-члены используют соглашение о вызовах thiscall (и вы даже не можете принять их адрес!).

Насколько я понимаю, вы на самом деле хотите, чтобы secondFunc() вызывала функцию-член определенного экземпляра класса (назовем ее this). Ну, адреса функций-членов всех экземпляров определенного класса одинаковы. Для того, чтобы передать указатель на объект, вам понадобится побочный канал. В этом случае это может быть статическая переменная. Или, если вам нужна поддержка MT, вам придется использовать локальное хранилище потоков (TLS),

Для этого требуется один обратный вызов для каждого члена типа SomeFunc, но вам все равно где-то понадобится диспетчер.

person ruslik    schedule 18.11.2010
comment
Как статический SomeFunc_callback_wrapper получает доступ к экземпляру obj? - person strager; 18.11.2010

Вы не можете вызвать функцию-член напрямую. Указатели на функции-члены не относятся к типу указателей на функции.

Вам нужно будет каким-то образом обернуть его в совместимую функцию. Однако, если ваша внешняя функция (та, которая принимает указатель на функцию в качестве аргумента) не является реентерабельной и не предоставляет дополнительный аргумент для использования указателем на функцию, вы не сможете передать экземпляр, на котором член функция работает, поэтому вы фактически не сможете сделать вызов.

person Jonathan Grynspan    schedule 18.11.2010
comment
Что ж, внешняя функция является частью библиотеки, и единственный способ, который я вижу, - это иметь статический массив этих указателей на функции... все функции, определенные мной и при вызове, просто вызывают функцию обратного вызова соответствующего объекта. Это действительно невозможно? - person Atmocreations; 18.11.2010
comment
@Atmocreations: да, это действительно невозможно. Чтобы понять, почему, обратите внимание, что помимо параметра void* параметру someFunc также необходимо значение this. Это как если бы у него было два параметра, один из которых может быть предоставлен только путем вызова функции-члена. secondFunc не вызывает функцию-член, как код должен определять this? - person Steve Jessop; 18.11.2010
comment
@Atmocreations: слава богу. Я надеялся, что вы не скажете, но это работает на Python/Lisp/другом языке с замыканиями ;-) - person Steve Jessop; 18.11.2010
comment
@Steve: C++ теперь имеет закрытие. По крайней мере, так мне говорит Apple. ;п - person Jonathan Grynspan; 18.11.2010
comment
@Jonathan: но лямбда-выражения C++0x не имеют того же типа, что и указатели на функции, и я думаю, что то же самое относится и к блокам, не так ли (никогда не использовал clang). Так что здесь нет ничего хорошего, и они в любом случае не являются правильными замыканиями, если они не продлевают срок службы своих значений. У него всегда были замыкания в том смысле, что вы могли вручную хранить (ссылаться) на целую кучу локальных переменных в функторе. - person Steve Jessop; 18.11.2010
comment
@Steve: На самом деле я имел в виду реализацию блоков Apple. :) Но да, ни один из них не может заменить указатель на функцию, подобный этому. Я предполагаю, что есть некоторые специфические для компилятора способы создания функции во время выполнения, которая оборачивает замыкание (или просто оборачивает вызов члена), но это становится очень грубой территорией. - person Jonathan Grynspan; 18.11.2010
comment
@Steve: хрррр, не забывай, я все еще могу спросить: P Я думаю, что нашел другой способ обойти это, но я должен подумать об этом дальше... - person Atmocreations; 18.11.2010

Существует обходной способ сделать это. Поскольку имена C++ искажены, вы не можете напрямую использовать его для нестатических функций. Однако, поскольку нестатические функции имеют те же подписи, что и функции C, вы можете напрямую использовать их в качестве обратных вызовов. Таким образом, для нестатических функций у вас могут быть статические оболочки функций. На этой странице подробно объясняется этот подход.

person Sridhar Iyer    schedule 18.11.2010
comment
сначала спасибо. выглядит интеллигентно, проверим детали позже. - person Atmocreations; 18.11.2010

См. этот образец:

#include <iostream>

class MyClass 
{
public: 
    MyClass()
    {
    }

    void* someFunc(void* param)
    {
        std::cout << "someFunc" << std::endl;
        return (void*)0;
    }
};

typedef void* (MyClass::*MemFun)(void*);

void secondFunc(int a, int b, MemFun fn)
{
    MyClass* ptr = 0;
    // This is dangerous! If the function someFunc do not operate the data in the class.
    // You can do this.
    (ptr->*fn)(0);
    std::cout << "Call me successfully!" << std::endl;
}

int main()
{
    secondFunc(1, 2, &MyClass::someFunc);

    system("pause");

    return 0;
}
person lj_enjoylife    schedule 18.11.2010

Не видел части о невозможности изменить вторую функцию раньше.

Определите структуру с указателями на функцию-член и на объект:

struct MyData {
   MyStruct  *myInstance;
   (void *)(MyStruct::myFunction)(void *data);
   void * dataPointer ;
}

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

void *proxyFunc( MyData *data)
{
  return (data->myInstance->*(data->myFunction))(data->dataPointer);
}

Затем вызовите функцию 2 как:

MyData  dataP = { someInstance, &MyStruct::someFunc, &dataPtr };
secondFunc(proxyFunc, &dataP);
person rldrenth    schedule 18.11.2010

использовать союз может быть легко

#include <iostream>

using namespace std;

class Data {
public:
    Data(const int data) : num(data) {

    }

    void printData() const {
        printf("%d", this->num);
    }

private:
    int num;
};

union Test {
    decltype(&Data::printData) member_fun;

    void (* normal_function)(...);
};

int main() {
    Test t;
    t.member_fun = &Data::printData;
    Data data(10);
    (*t.normal_function)(&data);
}
person Luna    schedule 21.06.2021