回调函数在构建和 DLL 时如何有用
Posted
技术标签:
【中文标题】回调函数在构建和 DLL 时如何有用【英文标题】:How Callback functions are useful while building and DLL 【发布时间】:2010-09-01 07:53:05 【问题描述】:回调函数是否等同于 C#(.NET) 中的事件。
我对回调函数的理解是,它是一个通过对该函数的引用来调用的函数。
示例代码为:
void cbfunc()
printf("called");
int main ()
void (*callback)(void);
callback=(void *)cbfunc;
callback();
return 0;
现在我不明白的是,关于从 DLL 通知客户端,这种用法如何充分。
假设我想在我的 DLL 方法 2() 上接收数据时执行 / 执行一些方法 1()。
与 .NET 中的事件进行任何比较都会很有帮助。
【问题讨论】:
【参考方案1】:回调和接口类是管理代码边界的好方法。它们有助于在代码中创建正式的边界和/或层,而不是将所有内容混为一谈。在处理大型软件解决方案时,这变得很有必要。
以下是如何使用回调和接口类的示例。在库/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
【讨论】:
【参考方案2】:您将指针传递给第 3 方例程(不必是 DLL),并在需要通知时通过克隆指针“回调”。
它类似于.net事件,后者也是一种回调。
顺便说一句,DLL 在 C++ 中不如在 .NET 中流行。这是由于无法共享静态变量(因此,单例),这个问题也称为缺乏动态链接(在 UNIX 系统中,这是通过共享对象解决的,这是一个完全不同的动态加载库的概念)。静态库提供了更好的代码重用策略。
【讨论】:
【参考方案3】:回调函数的目的与 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 范例,但暂时我们可以假设它实现了一个接受 TimerDelegate 的 SetTimer 函数。
在用户代码中,您可以在对象中将 MyTimerProc 定义为具有与委托匹配的签名的函数。并像这样调用它
TimerDelegate d = new TimerDelegate(myObject.MyTimerProc);
SetTimer(0,0,delay,d);
或者,如果“timers”是与 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);
【讨论】:
【参考方案4】:一个值得注意的例子是 qSort(),它需要一个回调函数来比较 2 个项目。 回调函数可以在运行时决定一些行为。
如果某些行为应该由客户端在运行时动态决定,那么我会要求客户端提供回调函数。
【讨论】:
以上是关于回调函数在构建和 DLL 时如何有用的主要内容,如果未能解决你的问题,请参考以下文章