std::function可調用對象包裝器
C++可調用對象(Callable Objects)定義如下:
- 函數指針:與C語言一致;
- 類成員函數指針;
- 仿函數(functor):也成函數對象,重載
operator()
運算符的類/結構體對象; - lambda表達式。
std::function是Callable Objects的包裝器(Wrapper),可接收除了類成員函數指針以外的任意Callable Objects。std::function可用來處理函數回調,與C語言函數指針類似,允許保存以上Callable Objects,並延遲執行它們,但它可保存除函數指針外的其他Callable Objects,因此它比C語言更強大。
當我們為std::function具現化一個函數簽名(函數類型,包括返回值和參數列表),它就成為一個可容納所有這類調用的函數包裝器。
std::function基本用法
#include <iostream>
#include <functional> // std::function
// global function
void func(void) {
std::cout << __FUNCTION__ << std::endl;
}
class Foo {
public:
// class static function
static int foo_func(int a) {
std::cout << __FUNCTION__ << "(" << a << "):";
return a;
}
// class non-static member function
int foo_func_nonstatic(int a) {
std::cout << __FUNCTION__ << "(" << a << "):";
return a;
}
};
class Bar {
public:
// functor
int operator()(int a) {
std::cout << __FUNCTION__ << "(" << a << "):";
return a;
}
};
int main() {
// 傳入合適函數簽名給std::function模板參數即可綁定對應簽名的
// 普通函數或
// 類靜態成員函數或
// 借助std::bind綁定類非靜態成員函數
std::function<void(void)> func1 = func;
std::function<int(int)> func2 = Foo::foo_func;
Foo foo;
std::function<int(int)> func3 = std::bind(&Foo::foo_func_nonstatic, &foo,
std::placeholders::_1);
// 然后,直接像函數一樣調用
func1(); // func
std::cout << func2(1) << std::endl; // foo_func(1):1
std::cout << func3(11) << std::endl; // foo_func_nonstatic(11):11
// 當函數簽名一致時,func2可像一個變量一樣復用
// Bar重載了operator()即成為functor,可直接包裝到std::function
Bar bar;
func2 = bar;
std::cout << func2(2) << std::endl; // operator()(2):2
// 也可綁定lambda表達式
auto func_lambda = [](int a){
std::cout << "bind lambda sample(" << a << ")" << std::endl;
};
func_lambda(3); // bind lambda sample(3)
return 0;
}
既然std::function可作為左值接收函數對象,那么它可作為函數的形參;且std::function可綁定函數指針,實現函數的延遲執行,所以std::function可取代std::function作為回調函數。
std::function實現回調機制的例子如下:
#include <iostream>
#include <functional> // std::function
// 任意可調用對象,如普通全局函數
void func_callback(int a) {
std::cout << __FUNCTION__ << ":Output, a=" << a << std::endl;
}
class Foo {
public:
explicit Foo(std::function<void(int)> cb)
:cb_(cb), a_(0) {
}
~Foo() = default;
// setter
void set_a(int a) {
a_ = a;
}
void OutputCallback() {
cb_(a_);
}
private:
std::function<void(int)> cb_;
int a_;
};
int main() {
// 實例化Foo,並參數注入回調函數
// 處理
// 回調輸出處理結果
Foo foo(func_callback);
foo.set_a(1);
foo.OutputCallback(); // func_callback:Output, a=1
return 0;
}
以上,介紹了std::function綁定Callable Objects的用法,並以一個實例演示了std::function作為函數回調的例子;其中,func_callback回調函數參數為int型,在實際應用如人臉智能分析中,人臉分析信息結構體指針可作為回調函數參數,該函數可輸出人臉識別結果;Foo類可為人臉分析相關類,OutputCallback可能在該類某個人臉分析線程中被調用,分析得到的結果可調用OutputCallback回調輸出給提供func_callback的用戶。
注:C++11提供的std::function可替代C語言中函數指針作為回調函數,前者是C++編程風格,后者是C編程風格。
C/C++回調機制在服務器端並發編程和游戲領域應用廣泛,著名2d游戲框架Cocos2dx大量使用回調機制,提供了常用的回調宏如下所示,更多回調機制可參考Cocos2dx開源源碼。
// Cocos2dx new callbacks based on C++11
//
// __selector__:回調函數指針
// __target__:回調對象指針
// ##__VA_ARGS__:可變參數列表
// std::placeholders::_1:不定參數1,調用時由調用函數的參數傳入
// std::placeholders::_2:不定參數2,調用時由調用函數的參數傳入
// std::placeholders::_3:不定參數3,調用時由調用函數的參數傳入
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
std::function/std::bind與抽象工廠、工廠方法的一點思考
TODO...