Callback Function
从行为上概括,回调函数就是用函数指针去调用函数。这样起到的作用与直接调用函数相似,但是灵活性大大增加。
平时函数的参数是变量的值或变量的指针,调用者就只能传递这个有限的信息;而如果函数的参数可以是其他函数的指针,调用者就向被调用者传递了一个动作。
举个我们最熟悉的例子,C语言的stdlib.h中的qsort函数的原型是
1 | void qsort (void* base, size_t num, size_t width, int(__cdecl* compare)(const void*, const void*)); |
在我们调用qsort的时候,不仅可以传递数组的基地址、数组的元素个数、每个元素占用的字节数这些基本信息,还可以传递“比较”这个动作。
从库函数编写者的角度看更为明显,我需要编写一个函数foo实现功能A,但是功能A的实现过程中有部分动作是不确定的,由foo的调用者决定,就用函数指针做参数来实现。
总结:实体A提供函数a的指针,调用函数B,B实现的时候就**回调(Callback)**了函数a
意义
从面向对象的角度看,如果是为了实现上述的灵活性,应该尽量避免使用回调函数,去利用面向对象的特性。
现在我们利用回调是因为可以异步实现,有利有弊。由于本人水平有限,更多的例子可以参考知乎问题 回调函数(callback)是什么?
Callback Function in GLUT
Program Format
初学openGL,照着例程的格式写出了这样的程序
1 | void graph (void) { |
显然,最后的 glutDisplayFunc()
与glutMainLoop()
是将画好的图形显示出来的关键。
Process of Execution
这里的glutDisplayFunc()
属于回调函数,而glutMainLoop()
的作用是进入一个死循环,不断检测程序之前注册的那些回调函数是否应该被调用,类似**轮询(polling)**机制。
对于glutDisplayFunc()
来说,为了防止画面不停绘制占用CPU资源,只有在以下情况发生时才会再次执行
- 窗口大小改变
- 某个回调函数调用了
glutPostRedisplay()
而其他回调函数则会在其对应的时候被触发并执行
Sample
先给出glut中那些 回调函数的原型
glutKeyboardFunc()
Prototype
1 | void glutKeyboardFunc (void (*func)(unsigned char key, int x, int y)); |
Called
当按下的是字母键等有对应的ASCII码的键时被调用
Parameter
key:
按下的键
x, y:
鼠标的位置,注意原点(0, 0)为window的左上角
Code
1 | // control the square with 'w', 'a', 's', 'd' |
glutSpecialFunc()
Prototype
1 | void glutSpecialFunc (void (*func)(int key, int x, int y)); |
Called
当按下方向键等没有对应的ASCII码的键时被调用
Parameter
类似glutSpecialFunc()
Code
1 | // control the square with 'up', 'down', 'left', 'right' |
glutMouseFunc()
Prototype
1 | void glutMouseFunc (void (*func)(int button, int state, int x, int y)); |
Called
当鼠标左键/中键/右键被按下/释放时被调用
Parameter
button:
被按下/释放的是哪个键
state:
该键是被按下还是释放
x, y:
鼠标的位置,注意原点(0, 0)为window的左上角
Code
1 | // make the square big by click left mouse |
glutMotionFunc(), glutPassiveMotionFunc()
Prototype
1 | void glutMotionFunc (void (*func)(int x, int y)); |
Called
当鼠标在被按下的时候移动时,glutMotionFunc()
被调用;
当鼠标在松开的时候移动时,glutPassiveMotionFunc()
被调用;
Parameter
x, y:
鼠标的位置,注意原点(0, 0)为window的左上角
Code
1 | // drag the square by keep pressing the left mouse |
glutTimerFunc()
Prototype
1 | void glutTimerFunc (unsigned int msecs, void (*func)(int value), int value); |
Called
直接被调用,在使用时一般在被调用的函数最后进行递归调用,继续累计时间
这个函数不是在运行到它时立即执行,所以不算死循环
常用于render
在glutTimerFunc()
中设置了glutPostRedisplay()
后其他回调函数中可以不用再调用
Parameter
msecs:
时间间隔
value:
函数标记
Code
1 | // act with interval |
glutIdleFunc()
Prototype
1 | void glutIdleFunc (void (*func)(void)); |
Called
空闲时调用
应尽量减少空闲回调函数中的计算和渲染量,不然会影响其他部分的性能
Code
1 | # include <windows.h> // Sleep() in windows |
Public Part
这段代码是上文各回调函数定义的调用部分
1 | # include <gl\glut.h> |
Complete Program
几个有用的链接