如何在C和C++中实现回调

来源:互联网 发布:前端连接数据库代码 编辑:程序博客网 时间:2024/05/20 20:20
1  回调函数介绍

函数指针提供了回调函数的概念。现在我们试着用一个排序函数qsort来理解回调。这个函数根据用户指定的排序法则来对需要排序的field进行排序。需要排序的field可能是任何类型的;它通过一个void 类型的指针传递给qsort 进行排序。当然需排序的field的数量和每个field的size也必须被传递。现在问题是:既然需要排序的field的任何类型信息都没有被传递,那么qsort怎么对它们进行排序呢?答案很简单:qsort函数接收到指向一个比较函数的指针,这个比较函数的参数是两个void类型的指针,比较函数比较传入的两个变量,得出一个int类型的返回值。因此每当排序函数需要对比两个item的大小时,它就通过函数指针调用这个比较函数:这就是回调。

2  如何在C语言中实现回调

下面是排序函数 qsort 的声明:
     void qsort(void* field, size_t nElements, size_t sizeOfAnElement,
     
      int(_USERENTRY *cmpFunc)(const void*, const void*));

field 指向需要被排序的field的第一个成员,nElements 是field中需要被排序的items的数量,sizeOfAnElement 是以bytes为单位的需要排序的items的大小,cmpFunc是指向比较函数的指针,比较函数输入两个void类型的参数,返回一个int类型的值. 回调就像调用一个普通函数一样的被执行:你只需要使用函数指针的名字而不是函数名。


void qsort( ... , int(_USERENTRY *cmpFunc)(const void*, const void*))
{
/* sort algorithm - note: item1 and item2 are void-pointers */
int bigger=cmpFunc(item1, item2); // make callback
/* use the result */
}

3  使用排序函数 qsort 的范例代码

在下面的例子中一组 floats 类型的items被排序.

//-----------------------------------------------------------------------------------------
// 3 How to make a callback in C by the means of the sort function qsort


#include <stdlib.h> // due to: qsort
#include <time.h> // randomize
#include <stdio.h> // printf

// comparison-function for the sort-algorithm
// two items are taken by void-pointer, converted and compared

int CmpFunc(const void* _a, const void* _b)
{
// you've got to explicitly cast to the correct type
const float* a = (const float*) _a;
const float* b = (const float*) _b;

if(*a > *b) return 1; // first item is bigger than the second one -> return 1
else
if
(*a == *b) return 0; // equality -> return 0
else return -1; // second item is bigger than the first one -> return -1
}


// example for the use of qsort()
void QSortExample()
{
float field[100];

::randomize(); // initialize random-number-generator
for(int c=0;c<100;c++) // randomize all elements of the field
field[c]=random(99);

// sort using qsort()
qsort((void*) field, /*number of items*/ 100, /*size of an item*/ sizeof(field[0]),
/*comparison-function*/ CmpFunc);

// display first ten elements of the sorted field
printf("The first ten elements of the sorted field are .../n");
for(int c=0;c<10;c++)
printf("element #%d contains %.0f/n", c+1, field[c]);
printf("/n");
}

4  如何在一个C++静态成员函数中实现回调

和在C函数中实现回调是一样的。静态成员函数不需要对象来引用,因此实现起来和C相同。

5 如何在一个C++非静态成员函数中实现回调

Wrapper 方式

指向非静态成员函数的指针和通常的C函数指针是不一样的,因为它们需要类的this 指针才能够被传递。如果你只是需要对一个特定类的成员函数实现回调,那么你只需要把通常的C回调编码方式改成类成员函数的编码形式。但是,如果需要对任意一个类的非静态成员函数实现回调怎么办?这有一点点复杂。你需要写一个静态成员函数作为wrapper.静态成员函数和C函数的形式是一样的。然后你把指向包含有你希望调用的成员函数的对象的指针指向 void* 并把它作为一个附加参数或者全局变量传递给 wrapper。如果你使用全局变量方式,你得确认它必须指向正确的对象。当然你也必须把呼叫的参数传递给成员函数。wrapper 把void指针指向对应类的一个实例并且调用成员函数。下面我们有两个例子:

Example A: 指向类实例的指针作为附加参数被传递

函数 DoItA 对于类TClassA的对象实现回调。因此一个指向类TClassA的一个成员的指针以及一个指向静态wrapper函数 TClassA::Wrapper_To_Call_Display 的指针被传递给 DoItA。这个wrapper就是回调函数。


//-----------------------------------------------------------------------------------------
// 5 Example A: Callback to member function using an additional argument
// Task: The function 'DoItA' makes something which implies a callback to
// the member function 'Display'. Therefore the wrapper-function
// 'Wrapper_To_Call_Display is used.


#include <iostream.h> // due to: cout

class TClassA
{
public:

void Display(const char* text) { cout << text << endl; };
static void Wrapper_To_Call_Display(void* pt2Object, char* text);

/* more of TClassA */
};


// static wrapper-function to be able to callback the member function Display()
void TClassA::Wrapper_To_Call_Display(void* pt2Object, char* string)
{
// explicitly cast to a pointer to TClassA
TClassA* mySelf = (TClassA*) pt2Object;

// call member
mySelf->Display(string);
}


// function does something which implies a callback
// note: of course this function can also be a member function

void DoItA(void* pt2Object, void (*pt2Function)(void* pt2Object, char* text))
{
/* do something */

pt2Function(pt2Object, "hi, i'm calling back using a argument ;-)"); // make callback
}


// execute example code
void Callback_Using_Argument()
{
// 1. instantiate object of TClassA
TClassA objA;

// 2. call 'DoItA' for <objA>
DoItA((void*) &objA, TClassA::Wrapper_To_Call_Display);
}

Example B: 指向类实例的指针被存储在一个全局变量中

函数 DoItB 对于类TClassB的对象实现回调。一个指向静态函数 TClassB::Wrapper_To_Call_Display 的指针被传递给 DoItB. 这个wrapper被称作回调函数,它使用全局变量 void* pt2Object 并将其映射到 TClassB的一个实例上。非常重要的是:总是初始化一个全局变量并将其指向正确的类实例


//-----------------------------------------------------------------------------------------
// 5 Example B: Callback to member function using a global variable
// Task: The function 'DoItB' makes something which implies a callback to
// the member function 'Display'. Therefore the wrapper-function
// 'Wrapper_To_Call_Display is used.


#include <iostream.h> // due to: cout

void* pt2Object; // global variable which points to an arbitrary object

class TClassB
{
public:

void Display(const char* text) { cout << text << endl; };
static void Wrapper_To_Call_Display(char* text);

/* more of TClassB */
};


// static wrapper-function to be able to callback the member function Display()
void TClassB::Wrapper_To_Call_Display(char* string)
{
// explicitly cast global variable <pt2Object> to a pointer to TClassB
// warning: <pt2Object> MUST point to an appropriate object!

TClassB* mySelf = (TClassB*) pt2Object;

// call member
mySelf->Display(string);
}


// function does something which implies a callback
// note: of course this function can also be a member function

void DoItB(void (*pt2Function)(char* text))
{
/* do something */

pt2Function("hi, i'm calling back using a global ;-)"); // make callback
}


// execute example code
void Callback_Using_Global()
{
// 1. instantiate object of TClassB
TClassB objB;


// 2. assign global variable which is used in the static wrapper function
// important: never forget to do this!!

pt2Object = (void*) &objB;


// 3. call 'DoItB' for <objB>
DoItB(TClassB::Wrapper_To_Call_Display);
}

 
原创粉丝点击