У меня проблема с указателями на функции, и ничто из того, что я нашел в сети, не помогло мне решить эту проблему.
У меня есть функция из C API, которая берет указатель на функцию void:
extern int APIFunction(int, void (*func)(int));
У меня есть класс с функцией, которую я хотел бы поставить, когда я вызываю функцию API.
class MyClass
{
public:
void myFunction(int status, otherAPi arguments...);
};
Затем я создал указатель на мою функцию-член и создал новый экземпляр моего класса
typedef void (MyClass::*MyClassFunctionPointer)(int stat, otherAPi arguments...);
MyClassFunctionPointer fctPointer= &MyClass::myFunction;
LicenseSecurity instance;
Я получаю сообщение об ошибке при попытке вызвать функцию APi с указателем функции, который я создал:
int stat = APIFunction(5, fctPointer ); // -> error 1
int stat = APIFunction(5, instance.*fctPointer ); // -> error 2
Я получил ошибки соответственно в первом и втором случае:
E2034 Impossible to convert 'void (MyClass::*)(int, otherAPITypes...)' into 'void (*) (int, otherAPITypes...)'
E2342 Bad type correspondence in the parameter 'func' ('void (*)(int, otherAPITypes...)' desired, 'void(int, otherAPITypes...)' obtained)
У меня нет доступа к функции API, поэтому я не могу ее изменить. Резюмируя проблему: как получить «простой» указатель на функцию C для указания аргумента функции из функции-члена моего класса?
Спасибо
К сожалению, вы не можете. Сожалею.
В идеале, ваш API будет принимать что-то вроде std::function
это позволит вам обернуть свободные функции или функции-члены. Но если вы не можете изменить API, у вас нет другого выбора, кроме как предоставить бесплатную функцию.
Вы не можете получить «простой» указатель функции на нестатическую функцию-член, потому что функция требует указатель this при вызове. Если бы вы создали такой указатель на функцию, то при вызове функции не было бы этого указателя для ссылки.
С таким древним C API, к сожалению, у вас нет никакого способа сделать это.
Что вам нужно сделать, это сделать статическую функцию или функцию, не являющуюся членом, чтобы принимать обратный вызов, а затем выяснить, какой экземпляр объекта вызывать член. Некоторые C API позволяют передавать пользовательские данные в обратный вызов, и в этом случае вы используете их для хранения this
указатель в вопросе. Если это не вариант, вы можете использовать глобальный или одноэлементный объект и разрешить регистрацию только одного такого обратного вызова.
Вы можете объявить обратный вызов как отдельную функцию или как статический метод класса. Сложная часть обращается к указателю экземпляра класса внутри обратного вызова.
В идеале, хорошо разработанный API позволяет указывать пользовательское значение для обратных вызовов. Это позволяет вам легко передавать экземпляр класса и получать к нему доступ непосредственно внутри функции обратного вызова. Но, похоже, вы не работаете с таким API, поэтому вам нужно использовать обходной путь.
Если у вас одновременно используется только 1 экземпляр класса, вы можете сохранить указатель экземпляра в глобальной переменной, а для обратного вызова использовать глобальную переменную для доступа к экземпляру.
Но если у вас есть несколько экземпляров классов, используемых одновременно, вы ищете подходящее решение, похожее на VCL. MakeObjectInstance()
функция, которая позволяет TWndMethod
-подписанные методы класса, которые будут использоваться в качестве обратных вызовов процедур окна Win32. По сути, блок исполняемой памяти выделяется динамически, код блока-заглушки записывается в блок, а указатель экземпляра и указатель метода класса также сохраняются в блоке. Затем блок передается в API, как если бы это был указатель на функцию. Когда API вызывает «функцию», исполняется код заглушки, который должен управлять стеком вызовов и регистрами ЦП, чтобы вызывать указатель на метод сохраненного класса, передавая указатель на сохраненный экземпляр как его скрытый this
параметр, сохраняя семантику других параметров, стек вызовов, результат функции и т. д.
Ничто в C ++ на самом деле не обеспечивает такого рода громадного подхода. Это не сложно реализовать вручную, но это также не тривиально (взгляните на исходный код для MakeObjectInstance()
в исходном файле Classes.pas VCL). Самым сложным является создание необходимого кода-заглушки, который соответствует семантике сигнатуры вашего метода класса.