Это быстрое описание того, как адаптировать Ваши указатели функции C-стиля, если Вы кодируете на C++. Эта статья написана для того, чтобы предоставить быстрое описание людям, пытающимся выяснять, как работают функции обратного вызова на всевозможных языках. Щелкните здесь для C#/.NET версии. Или щелкните здесь, если Вы хотите сделать делегат C#-стиля функций обратного вызова в C++.
Проблема написания программы на C++ состоит в том, что работа с указателями функции реализована более сложно. В C все Ваши функции глобальны, и определить адрес функции можно в любой точке кода где это понадобится. Однако, в C ++ Ваши функции (если они не объявлены как статичные) связаны с объектами, и Вы должны оперировать объектом наряду со своим указателем функции.
Есть по крайней мере два варианта:
Функции обратного вызова унаследованные от интерфейса обратного вызова.
Например, требуется передать указатель на функцию следующего вида:
Сначала, необходимо сделать объект который содержит вышеописанную функцию:
Теперь создадим класс, содержащий функцию обратного вызова, унаследованный от нового объекта.
Реализация внутренних функций обратного вызова.
Это "официальный" способ сделать это в Java. Вместо того, чтобы отдельно реализовывать интерфейс обратного вызова и класс, реализована часть объекта в классе (в приватной части) который реализует интерфейс.
Это хороший способ, т.к. он позволяет обращаться к множеству методов того же объекта; можно сделать следующее:
Вышеупомянутый пример показывает. что можно можно вызвать одну и туже callback-функцию по-разному в одном и том же объекте. Это более громоздко в плане написания, но не требует никакой дополнительной предварительной обработки.
Функции обратного вызова в COM.
Если требуется сделать это в COM, необходимо заставить класс быть похожим на COM-класс, т.е. на IUnknown, а так же соответствующий интерфейс обратного вызова. Это означает, что требуется три дополнительных метода: QueryInterface, AddRef и Release.
Для примера приведем реализацию интерфейса ISampleGrabberCB от DirectShow / WDM video capture API.
Есть вероятно более простой способ реализации. Можно было бы переделать QueryInterface(), чтобы он делал нечто похожее, но объект выше довольно прост и не делает ничего необычного с COM (в частности, все его интерфейсы указывают на один и тот же объект):
Данная статья является переводом.
Статья-исходник находиться тут
Проблема написания программы на C++ состоит в том, что работа с указателями функции реализована более сложно. В C все Ваши функции глобальны, и определить адрес функции можно в любой точке кода где это понадобится. Однако, в C ++ Ваши функции (если они не объявлены как статичные) связаны с объектами, и Вы должны оперировать объектом наряду со своим указателем функции.
Есть по крайней мере два варианта:
- Добавить статический метод объекта и передать указатель на него (это будет идентично C-функции), а так же указатель "this" (чтобы указать на объект который необходимо вызвать). После этого можно вызвать эту функцию из другой функции в созданном объекте.
- Сделать интерфейс (чистый виртуальный класс).
Функции обратного вызова унаследованные от интерфейса обратного вызова.
Например, требуется передать указатель на функцию следующего вида:
void FooCallback(int a, int b, int c);
Сначала, необходимо сделать объект который содержит вышеописанную функцию:
class IFooCallback
{
public:
virtual void FooCallback(int a, int b, int c);
};
Теперь создадим класс, содержащий функцию обратного вызова, унаследованный от нового объекта.
class NeedsToBeCalled : public IFooCallback
{
public:
void FooCallback(int a, int b, int c);
{
do_something();
}
void MyFunction()
{
// Now pass 'this' as the callback object
SetCallback(this);
}
};
Реализация внутренних функций обратного вызова.
Это "официальный" способ сделать это в Java. Вместо того, чтобы отдельно реализовывать интерфейс обратного вызова и класс, реализована часть объекта в классе (в приватной части) который реализует интерфейс.
class NeedsToBeCalled
{
private:
class InternalCallback : public IFooCallback
{
void FooCallback(int a, int b, int c);
{
do_something();
}
} myCallback;
public:
void MyFunction()
{
// Now pass &myCallback as the callback object
SetCallback(&myCallback);
}
};
Это хороший способ, т.к. он позволяет обращаться к множеству методов того же объекта; можно сделать следующее:
class GetsCalledSeveralTimes
{
private:
class FirstInternalCallback : public IFooCallback
{
void FooCallback(int a, int b, int c);
{
do_something();
}
} firstCallback;
class SecondInternalCallback : public IFooCallback
{
void FooCallback(int a, int b, int c);
{
do_something_else();
}
} secondCallback;
public:
void MyFunction()
{
// Now we have a choice of objects to set as the callback:
SetCallback(&firstCallback);
// Or:
SetCallback(&secondCallback);
}
};
Вышеупомянутый пример показывает. что можно можно вызвать одну и туже callback-функцию по-разному в одном и том же объекте. Это более громоздко в плане написания, но не требует никакой дополнительной предварительной обработки.
Функции обратного вызова в COM.
Если требуется сделать это в COM, необходимо заставить класс быть похожим на COM-класс, т.е. на IUnknown, а так же соответствующий интерфейс обратного вызова. Это означает, что требуется три дополнительных метода: QueryInterface, AddRef и Release.
Для примера приведем реализацию интерфейса ISampleGrabberCB от DirectShow / WDM video capture API.
private:
//! COM reference count
ULONG _ref_count;
public:
//! Return a ptr to a different COM interface to this object
HRESULT APIENTRY QueryInterface( REFIID iid, void** ppvObject )
{
// Match the interface and return the proper pointer
if (iid == IID_IUnknown) {
*ppvObject = dynamic_cast(this);
} else if (iid == IID_ISampleGrabberCB) {
*ppvObject = dynamic_cast(this);
} else {
// It didn't match an interface
*ppvObject = NULL;
return E_NOINTERFACE;
}
// Increment refcount on the pointer we're about to return
this->AddRef();
// Return success
return S_OK;
}
//! Increment ref count
ULONG APIENTRY AddRef()
{
return (++_ref_count );
}
//! Decrement ref count
ULONG APIENTRY Release()
{
return (--_ref_count );
}
Есть вероятно более простой способ реализации. Можно было бы переделать QueryInterface(), чтобы он делал нечто похожее, но объект выше довольно прост и не делает ничего необычного с COM (в частности, все его интерфейсы указывают на один и тот же объект):
HRESULT APIENTRY QueryInterface(REFIID iid, void** ppvObject)
{
*ppvObject = this;
this->AddRef();
return S_OK;
}
Данная статья является переводом.
Статья-исходник находиться тут
Комментариев нет:
Отправить комментарий