0 Preface
Friends who have used SDK should know the concept of "callback function", but this article does not describe how to use the callback function, but stand on the perspective of the SDK developer, tell how to implement the callback mechanism.
What is a callback (Callback)
The callback is that the client program c calls a function A in the service program S, then S again, in turn to call a function B in C, for C, this B is called a callback function. For example, the window process function under Win32 is a typical callback function.
Generally speaking, c does not call B, and the purpose of providing B is to let S to call it, and C has to be available. Since S does not know that C is called a very name, S will agree to b's interface specification (function prototype), then tell S by c to a function R in advance, tell S you will use B function, this process is called a callback The registration of the function, R is called a registration function.
The following is a popular example:
For a certain day, I called you, of course, a problem, :), you can't think of a solution, I can't take the phone there, so we agree: wait for you to think of the way after thinking I, this way, I hang up the phone to do other things. After XX minutes, my mobile phone rang, the question of your life is so good, it should be treated so. The story is over.
This example illustrates the programming mode of "asynchronous callback". Among them, you will later tell me the result is a "callback" process; my mobile phone number must tell you before, this is a registered callback function; my mobile phone number should be effective and the phone can receive your call, this It is the callback function to meet the interface specification.
2. Use the callback without
If you are an SDK user, once you have set a callback mechanism, then you are forced to use the callback function, so this problem is only meaningful for the SDK designer. From the purpose of the introduction, the callback is roughly divided into three:
1) SDK has messages that need to notify the application, such as the timer being triggered;
2) The implementation of the SDK requires the participation of the application, such as the SDK requires you to provide a sorting algorithm;
3) The operation of the SDK is time consuming, but the application cannot be blocked there, so use asynchronous mode, let the calling function return, the SDK is executed in the background, and the result is notified after the operation is completed.
Summary above, you may suddenly realize: the original "callback mechanism" is everywhere!
Yes, not only Win32 API programming you will use, nor is it used in other SDK programming, usually we can use a callback mechanism when writing programs yourself. At this time, we are both callback designers again. Users.
3. Traditional SDK callback function design mode
Win32 SDK is an exemplary example in this regard. This type of SDK's function interface is based on C language, SDK, or provides a special registration function for registering the address of the callback function, or when a method is called. The address of the function, the prototype of the callback function is also bound due to function pointer in the registration function.
Taking the multimedia timer function in Win32 as an example, its prototype is:
MmResult TimeSetEvent (
Uint udelay,
/ / Timer interval, in milliseconds
Uint UResolution,
LPTIMECALLBACK LPTIMEPROC,
// Tune function address
DWORD DWUSER,
// User set data
Uint FuEvent
);
The third parameter is used to register the callback function, and the fourth parameter is used to set the user-defined data, and the role will be described later, and the definition of LPTIMEFCALLBACK is: Typedef
Void
Callback TimeCallback (uint utimerid, uint UMSG, DWORD DWUSER, DWORD DW1, DWORD DW2);
TypeDef TimeCallback Far * LPTIMECALLBACK;
Therefore, the user-defined callback function must have the above-specified function prototype, the following is the specific method of use of the callback function:
#include
"stdio.h"
#include
"windows.h"
#include
"mmsystem.h"
// Multimedia timer needs to include this file
#pragma comment (Lib,
Winmm.lib ")
// Multimedia timer needs to import this library
Void
Callback Timer_Proc (uint utimerid, uint umsg, dWord dwuser, dword dw1, dword dw2)
/ / Define the callback function
{
PRINTF
"Time Out./N");
}
int main ()
{
Uint Nid = TimeSetEvent (1000, 0, Timer_Proc, 0, Time_Callback_Function | TIME_PERIODIC);
// Register a callback function
GetChar ();
Timekillevent (NID);
Return 0;
}
Run the program, we will see that "Time Out" information will be printed every other second. At the same time, we should also notice that the TimeSetEvent here is asynchronously. TimeSetEvent is running quickly. The main thread continues to execute, and the operating system is responsible for checking the Timer in the background.
As mentioned earlier, this article is to look at the problem at the perspective of the SDK designer. Here, we will turn the previous example into procedures, as follows:
/// sdk.h (SDK header file)
#ifndef __sdk_h__
#define __sdk_h__
Typedef
Void (* help_callback) (const)
Char *);
// Tune function pointer
Void Help_me (Const
Char * question, help_callback
Callback);
// Interface declaration
#ENDIF
// __ SDK_H__
/// sdk.cpp (SDK source file, for convenience, no .c file)
#include
"sdk.h"
#include
"stdio.h"
#include
"windows.h"
Help_callback g_callback;
Void do_it ()
// Processing function
{
PRINTF
"Thinking ... / N");
Sleep (3000);
PRINTF
"Think Out./N");
PRINTF
"Call Him./N");
g_callback
"2." );
}
Void Help_me (Const
Char * question, help_callback
Callback)
// Interface implementation
{
g_callback =
Callback;
/ / Save a callback function pointer
PRINTF
"Help_me:% S / N", Question;
DO_IT ();
// If you use asynchronous, it is generally used to create a thread.
}
/// app.cpp (application source file)
#include
"sdk.h"
#include
"stdio.h"
Void got_answer (const
Char * msg)
/ / Define the callback function
{
PRINTF
"GOT_ANSWER:% S / N", MSG);
}
int main ()
{
Help_me
"1 1 =?", Got_answer;
// Use SDK, register the callback function
Return 0;
}
4. Design of callback function in C
The C class can also be used in similar ways. If the SDK uses a C language interface, the application uses a C programming method, and the class member function cannot assign a common function pointer due to an implicit THIS pointer, and the solution is simple, which is to add Static keywords. Take the above program, here we only look at the application code:
/// app.cpp (c style)
#include
"sdk.h"
#include
"stdio.h"
Class app
{
PUBLIC:
Void ask
Char * question)
{
Help_me (question, got_answer);
}
Static
Void got_answer (const
Char * msg)
{
PRINTF
"GOT_ANSWER:% S / N", MSG);
}
}
int main ()
{
AppAp;
app.ask
"1 1 =?");
Return 0;
}
There is a significant shortcoming above: Since Got_answer is a static member function, it cannot access non-static member variables of the class, which is not a good thing. In order to solve this problem, as designers of the callback function, it is necessary to add a parameter to the value required to pass the user, as follows:
/// sdk.h (SDK header file)
#ifndef __sdk_h__
#define __sdk_h__
Typedef
Void (* help_callback) (const)
Char *,
unsigned
Long);
// Tune function pointer
Void Help_me (Const
Char * question, help_callback
Callback,
unsigned
Long user_value;
// Interface declaration
#ENDIF
// __ SDK_H__
/// sdk.cpp (SDK source file, for convenience, no .c file)
#include
"sdk.h"
#include
"stdio.h"
#include
"windows.h"
Help_callback g_callback;
unsigned
Long g_user_value;
Void do_it ()
{
PRINTF
"Thinking ... / N");
Sleep (3000);
PRINTF
"Think Out./N");
PRINTF
"Call Him./N");
g_callback
"2.", g_user_value);
/ / Inferred data set by the user
}
Void Help_me (Const
Char * question, help_callbackcallback,
unsigned
Long User_Value)
{
g_callback =
Callback;
g_user_value = user_value;
/ / Save the data set by the user
PRINTF
"Help_me:% S / N", Question;
DO_IT ();
}
/// app.cpp (application source file)
#include
"sdk.h"
#include
"stdio.h"
#include
Assert.h "
Class app
{
PUBLIC:
App (Const
Char * name): m_name (name)
{
}
Void ask
Char * question)
{
Help_me (Question, Got_answer,
unsigned
Long)
THIS);
// Incorporate this pointer
}
Static
Void got_answer (const
Char * msg,
unsigned
Long User_Value)
{
App * pTHIS = (app *) user_value;
/ / Convert to this pointer to access non-static data members
Assert (pthis);
PRINTF
"% s got_answer:% s / n", pthis-> m_name, msg);
}
protected:
Const
Char * m_name;
}
int main ()
{
App APP
"Abc");
app.ask
"1 1 =?");
Return 0;
}
The user_value here is designed as unsigned long, which can pass integer data or one address value (because they have a width of 32 on the 32-bit machine), with address, such as structural variables, class objects, etc. You can visit it.
5. C backmoding programming mode
The times are constantly progressing, SDK is no longer an old API interface, C object-oriented programming is widely used in various libraries, so the callback mechanism can also be implemented with some of C features.
Through the foregoing explanation, it is actually that the essence of the callback is: SDK defines a set of interface norms, and the application implements it in accordance with the regulations. This is a very simple, think about inheritance in our C , think about our dear abstract base class ... so we get the following code:
/// sdk.h
#ifndef __sdk_h__
#define __sdk_h__
Class Notifier
// Tune class, the application needs to be derived
{
PUBLIC:
Virtual ~ notifier () {}
Virtual
Void got_answer (const
CHAR * ANSWER) = 0;
// Pure virtual function, the user must implement it
}
Class SDK
// SDK provides a service class
{
PUBLIC:
SDK (Notifier * pNotifier);
/ / The user must register pointers to the callback class
Void Help_me (Const
Char * question);
protected:
Void do_it ();
protected:
Notifier * M_PNotifier;
// Mark the pointer to save the callback class
}
#define
// __ SDK_H__
/// sdk.cpp
#include
"sdk.h"
#include
"windows.h"
#include
Using
Namespace
STD;
SDK :: SDK (Notifier * PNOTIFIER): M_PNotifier (PNOTIFIER)
{
}
Void SDK :: HELP_ME (Const
Char * question)
{
Cout <<
"Help_me:" << Question << Endl;
DO_IT ();
}
Void SDK :: DO_IT ()
{
Cout <<
"thinking ..." << endl;
Sleep (3000);
Cout <<
"Think out." << endl;
Cout <<
"Call Him" << ENDL;
M_PNotifier-> Got_answer
"2." );
}
/// app.cpp
#include
"sdk.h"
Class app:
Public Notifier
// Application implements a class derived from the callback class
{
PUBLIC:
App (Const
Char * name): m_sdk
this), M_Name (Name)
// Incorporate this pointer
{
}
Void ask
Char * question)
{
m_sdk.help_me (question);
}
Void got_answer (const
Char * answer)
/ / Implement a pure virtual interface
{
COUT << m_name <<
"Got_answer:" << answer << Endl;
}
protected:
SDK M_SDK;
Const
Char * m_name;
}
int main ()
{
App APP
"Abc");
app.ask
"1 1 =?");
Return 0;
}
Haha, is it very cool? Notifier defines the callback function that the user must implement is defined in a pure virtual function, so that the user has to implement it, of course, if it is not unreaged, then we can also define it into a general virtual function, as the destructor Provide an "empty" implementation so that users can implement it when they care about it. Since this type of role is a callback, we may wish to call it a callback class.
This way is simplified, you can combine SDK with Notifier together, @ #% & @ # ..., stop, such code is available on C books per C , and use it (don't dock, Eggs can, Tomatoes don't want).
6. C class template mode
The above way has inheritance and virtual functions, some friends may not like the extra price from them, so how can they eliminate these two? The title of this section has given an answer - class template, code is:
/// sdk.h
#ifndef __sdk_h__
#define __sdk_h__
#include
Using
Namespace
STD;
Template <
Typename Notifier>
Class SDK
{
PUBLIC:
SDK (Notifier * PNOTIFIER); Void Help_me (Const
Char * question);
protected:
Void do_it ();
protected:
Notifier * M_PNotifier;
}
Template <
Typename Notifier>
SDK
{
}
Template <
Typename Notifier>
Void SDK
Char * question)
{
Cout <<
"Help_me:" << Question << Endl;
DO_IT ();
}
Template <
Typename Notifier>
Void SDK
{
Cout <<
"thinking ..." << endl;
Sleep (3000);
Cout <<
"Think out." << endl;
Cout <<
"Call Him" << ENDL;
M_PNotifier-> Got_answer
"2." );
}
#ENDIF
// __ SDK_H__
/// app.cpp
#include
"sdk.h"
Class app
{
PUBLIC:
App (Const
Char * name): m_sdk
this), M_Name (Name)
{
}
Void ask
Char * question)
{
m_sdk.help_me (question);
}
Void got_answer (const
Char * answer)
{
COUT << m_name <<
"Got_answer:" << answer << Endl;
}
protected:
SDK
Const
Char * m_name;
}
int main ()
{
App APP
"Abc");
app.ask
"1 1 =?");
Return 0;
}
Since there are very few compiler support templates, forced us to put the SDK's implementation in the header file, which is not a good choice for friends who want to hide SDK specific implementation. So this approach is still used in SDK.
7. End
Knowledge is flexible, depending on the specific needs, you can design a variety of wonderful modes and is not a thunder.
The summary of the callback will temporarily tell a paragraph, and then think of the new way in the future.
(Freefalcon at 2004.09.17)