Указатель функции на проблемы с функциями-членами класса
-
22-07-2019 - |
Вопрос
Прежде всего, я должен признать, что мои навыки программирования довольно ограничены, и я взял на себя (действительно небольшой) существующий ООП-проект C++, в который пытаюсь внедрить свои собственные разработки.К сожалению, у меня возникла проблема, выходящая за рамки моих знаний, и я надеюсь найти здесь помощь.Я работаю со сторонней библиотекой (которую нельзя изменить) для получения изображений с камеры и буду использовать здесь некоторые имена-заполнители.
Сторонняя библиотека имеет функцию «ThirdPartyGrab» для запуска непрерывного захвата в реальном времени и принимает указатель на функцию, которая будет вызываться каждый раз при поступлении нового кадра.Итак, в обычном приложении C это выглядит так:
ThirdPartyGrab (HookFunction);
«HookFunction» необходимо объявить как:
long _stdcall HookFunction (long, long, void*);
или «BUF_HOOK_FUNCTION_PTR», который объявлен как
typedef long (_stdcall *HOOK_FUNCTION_PTR) (long, long, void*);
Теперь у меня есть приложение на C++ и класс MyFrameGrabber, который должен инкапсулировать все, что я делаю.Поэтому я добавил функцию перехвата как частный член следующим образом:
long _stdcall HookFunction (long, long, void*);
Также в моем классе есть общедоступная функция void «StartGrab», которая должна запустить Grab.Внутри я пытаюсь позвонить:
ThirdPartyGrab (..., HookFunction, ...);
что (неудивительно) терпит неудачу.В нем говорится, что вызов функции MyFrameGrabber::HookFunction пропускает список аргументов, и мне следует вместо этого попытаться использовать &MyFrameGrabber::HookFunction для создания указателя.Однако передача «&MyFrameGrabber::HookFunction» вместо этого приводит к другой ошибке, которую невозможно преобразовать в BUF_HOOK_FUNCTION_PTR.
После прочтения Указатели на функции C++ FAQ Кажется, я понимаю проблему, но не могу найти решение.Я попытался сделать функцию-перехватчик статической, но это также привело к ошибке преобразования.Я также думал о том, чтобы разместить функцию-перехватчик вне класса, но мне нужно использовать функции класса внутри функции-перехватчика.Есть ли другой способ или мне нужно изменить всю концепцию?
РЕДАКТИРОВАТЬ 14.01.08:Я протестировал обходной путь синглтона, поскольку не могу изменить стороннюю библиотеку, а указатель void предназначен только для данных, которые используются внутри функции перехвата.К сожалению, из коробки это не сработало, как я надеялся....Я не знаю, должна ли статическая функция быть в отдельном классе, поэтому я поместил ее в свой класс «MyFrameGrabber»:
static MyFrameGrabber& instance()
{
static MyFrameGrabber _instance;
return _instance;
}
long Hook(long, long, void*); // Implementation is in a separate cpp file
В моем файле cpp у меня есть функция call_hook:
long MFTYPE call_hook(long x, MIL_ID y, void MPTYPE *z)
{
return MyFrameGrabber::instance().Hook(x,y,z);
}
void
MyFrameGrabber::grab ()
{
ThirdPartyGrab(..., call_hook, ...);
}
Но это дает мне ошибку в static MatroxFrameGrabber _instance;
что соответствующий стандартный конструктор не найден.Это правильно, потому что мой конструктор MyFrameGrabber выглядит так:
MyFrameGrabber (void* x,
const std::string &y, int z,
std::string &zz);
Я попытался добавить пустой конструктор MyFrameGrabber();
но это приводит к ошибке компоновщика.Должен ли я передавать пустые параметры конструктору MyFrameGrabber в синглтоне?Или мне нужен отдельный класс-перехватчик, и если да, то как я могу получить доступ к функциям MyFrameGrabber?Заранее спасибо.
ВТОРАЯ ПРАВКА 15.01.08:Я применил изменения, и теперь он компилируется и связывается.К сожалению, я пока не могу проверить это во время выполнения, потому что это DLL, и у меня еще нет Debug Caller Exe, и есть другие проблемы во время инициализации и т. д.Я отмечу это сообщение как ответ, потому что уверен, что это правильный способ сделать это.
Решение
Причина, по которой "&MyFrameGrabber::HookFunction" не может быть преобразована в BUF_HOOK_FUNCTION_PTR, заключается в том, что, будучи членом класса, он неявно имеет в качестве первого параметра указатель "this", поэтому вы не можете преобразовать функцию-член в функцию, не являющуюся членом:две подписи выглядят одинаково, но на самом деле они разные.
Я бы объявил интерфейс, определяющий вызываемую функцию, реализовал бы ваш класс и передал бы сам объект вместо обратного вызова (вы можете думать об интерфейсе как об объектно-ориентированной замене указателя функции):
class IHookInterface{
public:
virtual long HookFunction(long, long, void*) = 0;
};
class HookClass : public IHookInterface{
public:
virtual long Hook(long, long, void*) {
// your code here...
}
};
// новое определение:ThirdPartyGrab(..., IHookInterface, ...);
РЕДАКТИРОВАТЬ – другое возможное решение, если вы не можете изменить библиотеку:используйте синглтон, а не статическую функцию.
class HookClass{
public:
static HookClass& instance(){
static HookClass _instance;
return _instance;
}
long Hook(long, long, void*) {
// your code here...
}
};
long call_hook(long x,long y,void * z){
return HookClass::instance().Hook(x,y,z);
}
ВТОРОЕ РЕДАКТИРОВАНИЕ:вы можете немного изменить одноэлементный класс с помощью метода инициализации, чтобы вызвать конструктор с соответствующими параметрами, но, возможно, это не более элегантно, чем следующее решение, которое проще:
class HookClass{
public:
HookClass(string x,string y...){
}
long Hook(long, long, void*) {
// your code here...
}
};
static HookClass * hook_instance = 0;
long call_hook(long x,long y,void * z){
if (0 != hook_instance){
return hook_instance->Hook(x,y,z);
}
}
int main(){
hook_instance = new HookClass("x","y");
ThirdPartyGrab(..., call_hook, ...);
}
Другие советы
Ваш частный метод-член имеет неявное this
указатель в качестве первого аргумента.Если вы это запишете, то станет очевидно, что сигнатуры функций не совпадают.
Вам нужно написать статическую функцию-член, которую можно будет передать в качестве функции обратного вызова в библиотеку.Последний аргумент в пользу HookFunction
, а void*
, мне очень похоже на файл cookie, куда можно передать собственный указатель.
Итак, в целом должно быть примерно так:
class MyClass { long MyCallback(long, long) { // implement your callback code here } static long __stdcall ThirdPartyGrabCallback(long a, long b, void* self) { return reinterpret_cast<MyClass*>(self)->MyCallback(a, b); } public: void StartGrab() { ThirdPartyGrab(..., &MyClass::ThirdPartyGrabCallback, ..., this, ...); } };
Это, конечно, работает только в том случае, если void*
аргумент делает то, что я сказал.Позиция this
в ThirdPartyGrab()
call должно быть легко найти при наличии полной сигнатуры функции, включая имена доступных параметров.