Как функции обратного вызова полезны при сборке и DLL - PullRequest
0 голосов
/ 01 сентября 2010

Эквивалентны ли функции обратного вызова событиям в C # (.NET).

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

Пример кода будет:

void cbfunc()

{
        printf("called");
 }

int main ()

{

    void (*callback)(void);

    callback=(void *)cbfunc;

   callback();

   return 0;

}

Теперь я не понимаю, как это использовать в полной мере при уведомлении от DLL к клиенту.

Предположим, что я хочу выполнить / выполнить метод method1 (), когда получаю данные из моего метода DLL2 ().

Любое сравнение с событиями в .NET будет очень полезным.

Ответы [ 4 ]

1 голос
/ 01 сентября 2010

Функции обратного вызова выполняют аналогичную задачу для делегатов в C #.

Например, Win32 API предоставляет службу синхронизации, доступ к которой осуществляется посредством вызова SetTimer.SetTimer экспортируется системной DLL, но механизм точно такой же, как если бы он использовался в пользовательской dll.В вашем коде вы можете получить доступ к таймеру, выполнив что-то вроде этого:

void 
CALLBACK
MyTimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// do something
}

...

TIMERPROC fn = &MyTimerCallback;
int delay = 500;  
SetTimer(NULL,0,delay,fn); 

Вызов SetTimer и передача функции обратного вызова позволяет операционной системе вызывать функцию обратно каждый раз, когда срабатывает таймер.Конечно, здесь нет возможности многоадресной рассылки, и, особенно в случае SetTimer, функция обратного вызова должна быть функцией C или статическим методом класса.С этой функцией не связано ни одного экземпляра класса или объекта.

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

В пользовательском коде в объекте вы затем определяете MyTimerProc как функцию с сигнатурой, соответствующей делегату.И вызвать его так:

TimerDelegate d = new TimerDelegate(myObject.MyTimerProc);
SetTimer(0,0,delay,d);

Или, если «таймеры» были событием, которое соответствует TimerDelegate, то эквивалентный код C # будет выглядеть примерно так:

timers += new TimerDelegate(myObject.MyTimerProc);

Примечание: мойC # очень ржавый, поэтому не принимайте эти примеры кода в качестве какого-либо примера передового опыта или даже рабочего кода: P


При определении ваших собственных функций обратного вызова рекомендуется всегда определятьфункции обратного вызова принимают параметр void * "context", поскольку это позволяет программистам C ++ сохранять свой указатель "this" и извлекать его.

// the first parameter to the callback fn is a void* user supplied context parameter
typedef void (CALLBACK* MyCallbackFn)(void* context, int etc);

// and, the dll export function always takes a function pointer, context parameter pair.
DLL_EXPORT void SomeExportedFn(MyCallbackFn, void* context);
1 голос
/ 01 сентября 2010

Обратные вызовы и классы интерфейса - отличные способы управления границами кода. Они помогают создавать формальные границы и / или слои в вашем коде вместо того, чтобы объединять все вместе. Это становится необходимым при работе с большими программными решениями.

Ниже приведен пример использования обратных вызовов и классов интерфейса. В коде библиотеки / dll единственная вещь, которая должна быть доступна для основного исполняемого файла, это класс myddl_interface и функция getMyDllInterface (). Использование такого класса интерфейса полностью скрывает детали реализации от основного исполняемого файла. Класс интерфейса также позволяет основному исполняемому файлу зарегистрировать функцию, которая будет выполняться позже (т.е. обратный вызов).

// Begin library/dll Public Interface used by an executable
class mydll_interface {  
public:  
    typedef void (*callback_func_t)();  
public:  
    virtual void do_something() = 0;  
    virtual void registerFunction( callback_func_t ) = 0;  
};  

static mydll_interface* getMyDllInterface();

// End library/dll Public Interface used by an executable

// Begin library/dll Private implementation
class mydll_implementation : public mydll_interface {  
public:  
    void do_something() {  
        printf("Hello World\n");  
        _callback_func();  
    }  
    void registerFunction( callback_func_t c) {  
        _callback_func = c;  
    }  
private:  
    callback_func_t _callback_func;  
};  

static mydll_interface* getMyDllInterface() {  
    return new mydll_implementation();  
};  
// End library/dll Private implementation

// Begin main executable code
void myMainAppFunc() {  
    printf("hello World Again\n");  
}  

int main() {    
    mydll_interface* iface = getMyDllInterface();  
    iface->registerFunction(&myMainAppFunc);  
    iface->do_something();  
};  
// End main executable code
1 голос
/ 01 сентября 2010

Вы передаете указатель сторонней подпрограмме (не обязательно должна быть DLL), и он «вызывается обратно», когда требуется уведомление, клонированным указателем.

Это похоже на .net события в том, что последние также являются типом обратного вызова.

Кстати, библиотеки DLL менее популярны в C ++, чем в .NET. Это связано с невозможностью совместного использования статических переменных (и, следовательно, синглетонов), эта проблема также известна как отсутствие динамического связывания (в системах UNIX это решается с помощью общих объектов, которые представляют собой совершенно другую концепцию динамически загружаемой библиотеки). Статические библиотеки предлагают лучшую стратегию повторного использования кода.

0 голосов
/ 01 сентября 2010

Известный пример - qSort (), для сравнения 2 элементов требуется функция обратного вызова. Функция обратного вызова может оставить определенное поведение во время выполнения.

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

...