Designing Callbacks in C++

Designing Callbacks in C++

Function Pointers

首先来看一下什么是callback,callback实际上是一个函数,以参数的形式传递进另一个API中,在往后的某个时间点里面调用我们提供的callback。

callback的三种类型:

  • Function Pointer
  • Function Objects/Functors
  • Lambda functions

假设我们的框架中存在一个API可以从提供的原生数据构建一个API,API执行以下的步骤:

  1. 对原生数据添加头部和尾部;
  2. 加密;
  3. 返回信息;

现在这个API知道头部和尾部如何添加,但不了解如何进行加密,这个API可以接受一个函数指针回调如下:

1
2
3
4
5
6
7
8
9
10
std::string buildCompleteMessage(std::string rawData, std::string (* encrypterFunPtr)(std::string) )
{
// Add some header and footer to data to make it complete message
rawData = "[HEADER]" + rawData + "[FooTER]";

// Call the callBack provided i.e. function pointer to encrypt the
rawData = encrypterFunPtr(rawData);

return rawData;
}

我们提供的加密方式如下:

1
2
3
4
5
6
7
8
9
std::string encryptDataByLetterInc(std::string data)
{
for(int i = 0; i < data.size(); i++)
{
if( (data[i] >= 'a' && data[i] <= 'z' ) || (data[i] >= 'A' && data[i] <= 'Z' ) )
data[i]++;
}
return data;
}

那么我们的API就可以这样调用了:

1
2
std::string msg = buildCompleteMessage("SampleString", &encryptDataByLetterInc);
std::cout<<msg<<std::endl;

Function Objects & Functors

首先来看看什么是函数对象,实际上就是一个带状态的回调。一个重载了operator()的类对应的对象就是 Function Object or Functor,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
class MyFunctor
{
public:
int operator()(int a , int b)
{
return a+b;
}
};
//调用时
MyFunctor funObj;
std::cout<<funObj(2,3)<<std::endl;
funObj.operator()(2,3);

还是以上面的API为例子,假设我们系统API以三种不同的加密方式调用,加密方式分别是对每个字母加一、加二或者减一。如果是用函数指针的方式,我们需要定义三个不同函数,但实际上函数体是类似的。

但如果使用函数对象,那就可以在类里面绑定状态变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Encryptor {
bool m_isIncremental;
int m_count;
public:
Encryptor() {
m_isIncremental = 0;
m_count = 1;
}
Encryptor(bool isInc, int count) {
m_isIncremental = isInc;
m_count = count;
}
std::string operator()(std::string data) {
for (int i = 0; i < data.size(); i++)
if ((data[i] >= 'a' && data[i] <= 'z')
|| (data[i] >= 'A' && data[i] <= 'Z'))
if (m_isIncremental)
data[i] = data[i] + m_count;
else
data[i] = data[i] - m_count;
return data;
}
};

修改一下API的参数类型:

1
2
3
4
5
6
7
8
9
10
std::string buildCompleteMessage(std::string rawData,
Encryptor encyptorFuncObj) {
// Add some header and footer to data to make it complete message
rawData = "[HEADER]" + rawData + "[FooTER]";

// Call the callBack provided i.e. function pointer to encrypt the
rawData = encyptorFuncObj(rawData);

return rawData;
}

调用的时候只需要传递不同的参数到函数对象的构造器即可:

1
2
3
buildCompleteMessage("SampleString", Encryptor(true, 1));
// buildCompleteMessage("SampleString", Encryptor(true, 2));
// buildCompleteMessage("SampleString", Encryptor(false, 1));