(C/C++) Callback Function 回调(diao)函数
原文: http://www.codeguru.com/cpp/cpp/cpp_mfc/callbacks/article.php/c10557/Callback-Functions-Tutorial.htm
Callback Functions Tutorial
Introduction
If you are reading this article, you probably wonder what callback functions are. This article explains what callback functions are, what are they good for, why you should use them, and so forth. However, before learning what callback functions are, you must be familiar with function pointers. If you aren't, consult a C/C++ book or consider reading the following:
- The Syntax of C and C++ Function Pointers
- Pointers to member functions
- Declaring, Assigning, and Using Function Pointers
(注:关键就是要理解函数指针)
What Is a Callback Function?
The simple answer to this first question is that a callback function is a function that is called through a function pointer. If you pass the pointer (address) of a function as an argument to another, when that pointer is used to call the function it points to it is said that a call back is made.
(注:"回调函数"就是通过“函数指针”调用的函数.
通常这个"函数指针"会作为另外一个函数的一个参数,当这个指针被用来调用其指向的函数的时候,便产生了“回调”)
Why Should You Use Callback Functions?
Because they uncouple the caller from the callee. The caller doesn't care who the callee is; all it knows is that there is a callee with a certain prototype and probably some restriction (for instance, the returned value can be int, but certain values have certain meanings).
If you are wondering how is that useful in practice, imagine that you want to write a library that provides implementation for sorting algorithms (yes, that is pretty classic), such as bubble sort, shell short, shake sort, quick sort, and others. The catch is that you don't want to embed the sorting logic (which of two elements goes first in an array) into your functions, making your library more general to use. You want the client to be responsible to that kind of logic. Or, you want it to be used for various data types (ints, floats, strings, and so on). So, how do you do it? You use function pointers and make callbacks.
(注:想调用一个外部函数实现一些功能,但是这个外部函数又可以回来调用本函数实现部分逻辑,实现定制化。)
A callback can be used for notifications. For instance, you need to set a timer in your application. Each time the timer expires, your application must be notified. But, the implementer of the time'rs mechanism doesn't know anything about your application. It only wants a pointer to a function with a given prototype, and in using that pointer it makes a callback, notifying your application about the event that has occurred. Indeed, the SetTimer() WinAPI uses a callback function to notify that the timer has expired (and, in case there is no callback function provided, it posts a message to the application's queue).
Another example from WinAPI functions that use callback mechanism is EnumWindow(), which enumerates all the top-level windows on the screen. EnumWindow() iterates over the top-level windows, calling an application-provided function for each window, passing the handler of the window. If the callee returns a value, the iteration continues; otherwise, it stops. EnumWindows() just doesn't care where the callee is and what it does with the handler it passes over. It is only interested in the return value, because based on that it continues its execution or not.
However, callback functions are inherited from C. Thus, in C++, they should be only used for interfacing C code and existing callback interfaces. Except for these situations, you should use virtual methods or functors, not callback functions.
A Simple Implementation Example
Now, follow the example that can be found in the attached files. I have created a dynamic linked library called sort.dll. It exports a type called CompareFunction:
typedef int (__stdcall *CompareFunction)(const byte*, const byte*);
which will be the type of your callback functions. It also exports two methods, called Bubblesort() and Quicksort(), which have the same prototype but provide different behavior by implementing the sorting algorithms with the same name.
void DLLDIR __stdcall Bubblesort(byte* array,
int size,
int elem_size,
CompareFunction cmpFunc); void DLLDIR __stdcall Quicksort(byte* array,
int size,
int elem_size,
CompareFunction cmpFunc);
These two functions take the following parameters:
- byte* array: a pointer to an array of elements (doesn't matter of which type)
- int size: the number of elements in the array
- int elem_size: the size, in bytes, of an element of the array
- CompareFunction cmpFunc: a pointer to a callback function with the prototype listed above
The implementation of these two functions performs a sorting of the array. But, each time there is a need to decide which of two elements goes first, a callback is made to the function whose address was passed as an argument. For the library writer, it doesn't matter where that function is implemented, or how it is implemented. All that matters it is that it takes the address of two elements (that are the two be compared) and it returns one of the following values (this is a contract between the library developers and its clients):
- -1: if the first element is lesser and/or should go before the second element (in a sorted array)
- 0: if the two elements are equal and/or their relative position doesn't matter (each one can go before the other in a sorted array)
- 1: if the first element is greater and/or should go after the second element (in a sorted array)
With this contract explicitly stated, the implementation of the Bubblesort() function is this (for Quicksort(), which a little bit more complicated, see the attached files).
void DLLDIR __stdcall Bubblesort(byte* array,
int size,
int elem_size,
CompareFunction cmpFunc)
{
for(int i=; i < size; i++)
{
for(int j=; j < size-; j++)
{
// make the callback to the comparison function
if( == (*cmpFunc)(array+j*elem_size,
array+(j+)*elem_size))
{
// the two compared elements must be interchanged
byte* temp = new byte[elem_size];
memcpy(temp, array+j*elem_size, elem_size);
memcpy(array+j*elem_size,
array+(j+)*elem_size,
elem_size);
memcpy(array+(j+)*elem_size, temp, elem_size);
delete [] temp;
}
}
}
}
Note: Because the implementation uses memcpy(), these library functions should not be used for types other than POD (Plain-Old-Data).
On the client side, there must be a callback function whose address is to be passed to the Bubblesort() function. As a simple example, I have written a function that compares two integer values and one that compares two strings:
int __stdcall CompareInts(const byte* velem1, const byte* velem2)
{
int elem1 = *(int*)velem1;
int elem2 = *(int*)velem2; if(elem1 < elem2)
return -;
if(elem1 > elem2)
return ; return ;
} int __stdcall CompareStrings(const byte* velem1, const byte* velem2)
{
const char* elem1 = (char*)velem1;
const char* elem2 = (char*)velem2; return strcmp(elem1, elem2);
}
To put all these to a test, I have written this short program. It passes an array with five elements to Bubblesort() or Quicksort() along with the pointer to the callback functions.
int main(int argc, char* argv[])
{
int i;
int array[] = {, , , , }; cout << "Before sorting ints with Bubblesort\n";
for(i=; i < ; i++)
cout << array[i] << '\n'; Bubblesort((byte*)array, , sizeof(array[]), &CompareInts); cout << "After the sorting\n";
for(i=; i < ; i++)
cout << array[i] << '\n'; const char str[][] = {"estella",
"danielle",
"crissy",
"bo",
"angie"}; cout << "Before sorting strings with Quicksort\n";
for(i=; i < ; i++)
cout << str[i] << '\n'; Quicksort((byte*)str, , , &CompareStrings); cout << "After the sorting\n";
for(i=; i < ; i++)
cout << str[i] << '\n'; return ;
}
If I decide that I want the sorting to be done descending (with the biggest element first), all I have to do is to change the callback function code, or provide another that implements the desired logic.
Calling Conventions
In the above code, you can see the word __stdcall in the function's prototype. Because it starts with a double underscore, it is, of course, a compiler-specific extension, more exactly a Microsoft-specific one. Any compiler that supports development of Win32-based applications must support this or an equivalent one. A function that is marked with __stdcall uses the standard calling convention so named because all Win32 API functions (except the few that take variable arguments) use it. Functions that follow the standard calling convention remove the parameters from the stack before they return to the caller. This is the standard convention for Pascal. But in C/C++, the calling convention is that the caller cleans up the stack instead of the called function. To enforce that a function uses the C/C++ calling convention, __cdecl must be used. Variable argument functions use the C/C++ calling convention.
Windows adopted the standard calling convention (Pascal convention) because it reduces the size of the code. This was very important in the early days of Windows, when it ran on systems with 640 KB RAM.
If you don't like the word __stdcall, you can use the CALLBACK macro, defined in windef.h, as
#define CALLBACK __stdcall
or
#define CALLBACK PASCAL
where PASCAL is #defined as __stdcall.
You can read more about calling convention here: Calling Convetions in Microsoft Visual C++.
C++ Methods as Callback Functions
Because you probably write in C++, you want your callback function a method of a class. But, if you try this:
class CCallbackTester
{
public:
int CALLBACK CompareInts(const byte* velem1, const byte* velem2);
}; Bubblesort((byte*)array, , sizeof(array[]),
&CCallbackTester::CompareInts);
with a MS compiler, you get this compilation error:
error C2664: 'Bubblesort' : cannot convert parameter 4 from 'int (__stdcall CCallbackTester::*)(const unsigned char *,const unsigned char *)' to 'int (__stdcall *)(const unsigned char *,const unsigned char *)' There is no context in which this conversion is possible
That happens because non-static member functions have an additional parameter, pointer this (see this FAQ for more).
That obliges you to make the member function static. If that's not acceptable, you can use several techniques to overcome that. Check the following links to learn more.
Notices
The attached files contain two projects. SortingDLL is a Win32 DLL project. The sort.dll output library exports the two sorting functions, Bubblesort() and Quicksort(). The second project, SortDemo, is a Win32 Console Application that demonstrates how to use the sort.dll library. The output directory for both projects is Shared directory, where the following files can be found: sort.h, sort.dll, sort.lib, and SortDemo.exe.
Further References
(C/C++) Callback Function 回调(diao)函数的更多相关文章
- callback function(回调函数) - 术语
回调函数(CallBack Function)的定义: [todo] 下面是callback的一个场景,linux内核.LwIP里也有使用callback funcs.
- JavaScript callback function 回调函数的理解
来源于:http://mao.li/javascript/javascript-callback-function/ 看到segmentfault上的这个问题 JavaScript 回调函数怎么理解, ...
- Python 3.X 调用多线程C模块,并在C模块中回调python函数的示例
由于最近在做一个C++面向Python的API封装项目,因此需要用到C扩展Python的相关知识.在此进行简要的总结. 此篇示例分为三部分.第一部分展示了如何用C在Windows中进行多线程编程:第二 ...
- C语言中的回调函数(Callback Function)
1 定义和使用场合 回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数.函数是你实现 ...
- JS之Callback function(回调函数)
JS中的回调函数: 1.概念: 函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b,那么这个过程就叫回调,即把函数作为参数传入到另一个函数中,这个函数就是所谓的回调函数. 2.举例: ...
- C模块回调Lua函数的两种方法
作者:ani_di 版权所有,转载务必保留此链接 http://blog.csdn.net/ani_di C模块回调Lua函数的两种方法 lua和C通过虚拟栈这种交互方式简单而又可靠,缺点就是C做栈平 ...
- callback hell (回调地狱)
callback hell (回调地狱) callback(回调) 如何修复 callback hell callback 回调只是存放一些即将要处理的代码. 回调的执行顺序不是从上到下的,而是根据事 ...
- JavaScript callback function 理解
看到segmentfault上的这个问题 JavaScript 回调函数怎么理解,觉得大家把异步和回调的概念混淆在一起了.做了回答: 我觉得大家有点把回调(callback)和异步(asynchron ...
- JQuery ajax调用一直回调error函数
使用jquery的ajax调用,发现一直回调error函数,ajax调用代码如下,后台返回是正确的,为什么会报错呢? var descValue = $('#descEditArea').val() ...
随机推荐
- ros与下位机通信常用的c++ boost串口应用--22
摘要: 原创博客:转载请表明出处:http://www.cnblogs.com/zxouxuewei/ 一.首先移植c++ boost 库: 1. 先去 Boost官网 下载最新的Boost版本, 我 ...
- spark新能优化之数据本地化
数据本地化的背景: 数据本地化对于Spark Job性能有着巨大的影响.如果数据以及要计算它的代码是在一起的,那么性能当然会非常高.但是,如果数据和计算它的代码是分开的,那么其中之一必须到另外一方的机 ...
- 【BZOJ1008】【HNOI2008】越狱
以前水过的水题 原题: 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种.如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱 1& ...
- 使用 NGUI 实现头顶文字及血条
以下是 NGUI HUD Text 实现的: 基本原理: 1. 在角色头顶绑一个点 Pivot,用于对齐 2. 因为界面总是覆盖在人物头顶信息的上面,所以将 UIRoot 分为2个 Panel:1) ...
- svn常见错误总结
1. svn: Server sent unexpected return value (500 Internal Server Error) in response to OPTIONS reque ...
- Android中突发情况Activity数据的保存和恢复
Android中突发情况Activity数据的保存和恢复 写在前面:在我们的APP使用的过程中,总有可能出现各种手滑.被压在后台.甚至突然被杀死的情况.所以对APP中一些临时数据或关键持久型数据,就需 ...
- Axure轮播图
制作步骤如下: 1.新建一个动态面板,并添加几个动态面板状态(tab) 2.在所建的几个动态面板状态中添加要显示的每个广告的图片和相对应的代表第几个广告的数字. 3.添加页面交互事件.添加事件是在页面 ...
- 网络工程实训_2路由器基本配置及IOS介绍
实验2:路由器基本配置及IOS介绍.包括:CLI的使用与IOS基本命令:配置文件的备份和IOS的备份:CDP协议. 一.实验目的 1.熟悉路由器CLI的各种模式: 2.熟悉路由器CLI的各种编辑命令: ...
- openjudge-回文串判断【递归】
回文串判断 总时间限制: 1000ms 内存限制: 65536kB 描述 任意给定一个非空的字符串,判断其是否是回文串.回文串是指正向看和反向看均相等的串,如AbcDcbA和cDDc.如果是回文串,则 ...
- 【转】svn服务器IP修改后,本地怎么跟新svn同步,svn relocate 操作
本文来源:http://www.kukaka.org/home/showonews/444 1.进入工作复本 cd ~/test 2.查看仓库地址(URL) svn info 路径: . URL ...