0%

Lambda Expression in C++11

C++0x引進了Lambda Expression,這功能其實也不新鮮了,在其他語言都有類似的觀念。
為了處理Callback function,做了很多的改進
同樣的,我們從C語言時期看起。

C時期

C語言只有一種方式處理callback function,function pointer
像這個樣子

1
2
3
4
5
6
int add2(int a, int b) { return a + b; }
int operate(int a, int b, op2 func)
{
return func(a, b);
}
operate(3, 2, add2);

Function Pointer的方式處理Callback function非常經典,幾乎可以在所有純C語言開發的程式碼看到他。不過還是有可以改進的地方,例如

  • 速度: 由於傳進來的是指標,只能間接呼叫,無法有足夠的資訊做最佳化。
  • 狀態: Function比較難保存運行中的特定狀態。

C++98時期

C++的Functor(仿凾式)就是因此而生的,透過overload operator(),來改進上述所無法缺憾。搭配template的使用,使其應用更具彈性。可以同時支援Callback function跟Functor兩種形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Add2 {
int operator()(int a, int b)
{
return a + b;
}
};
int sub2(int a, int b) { return a - b; }
template <typename T>
int operate(int a, int b, T callback)
{
return callback(a, b);
}
operate(3, 2, Add2());
operate(3, 2, sub2);

之後有人想要將class member method跟static class method也包裝成Functor的形式,有LokiBoost兩大主流為主。這裡就不多談了,在C++0x中也將boost的bind跟function列入TR1裡,可以很方便的產生Functor。

C++0x時期

Functor雖好,不過每要新增一個新功能,就要寫一個function object,如果是只被使用一次的functor,維護這段程式碼需要付出成本,
因此就產生了一個暱名函數(anonymous function),像這樣。

1
2
3
4
5
6
7
8
for_each(v.begin(), v.end(), [](int n) {
cout << n;
if (n % 2 == 0) {
cout << " even ";
} else {
cout << " odd ";
}
});

詳細的語言規範可以參考MSDN的資料。透過跟程式碼上下文互動,達到非常有趣的效果。

Lambda Trick

之前我們介紹過unique_ptr,用來管理動態記憶體,非memory的Resource該怎麼處理,此時就是ScopeGuard登場的時候

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class ScopeGuard
{
public:
explicit ScopeGuard(std::function<void()> onExitScope)
: onExitScope_(onExitScope), dismissed_(false)
{ }

~ScopeGuard()
{
if(!dismissed_)
{
onExitScope_();
}
}

void Dismiss()
{
dismissed_ = true;
}

private:
std::function<void()> onExitScope_;
bool dismissed_;

private: // noncopyable
ScopeGuard(ScopeGuard const&);
ScopeGuard& operator=(ScopeGuard const&);
};
int main()
{
HANDLE h = CreateFile(...);
ScopeGuard onExit([&] { CloseHandle(h); });
// do smoething
return 0;
}

或者將不同function signature,由於要經過相同的處理流程,於是用lambda expression隱藏了細節,以下是DirectX ToolKit的部分程式碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template<typename T, typename TCreateFunc>
static T* DemandCreate(Microsoft::WRL::ComPtr<T>& comPtr, TCreateFunc createFunc)
{
T* result = comPtr.Get();

if (!result)
{
// Create the new object.
ThrowIfFailed(
createFunc(&result)
);
comPtr.Attach(result);
}
return result;
}
ID3D11SamplerState* CommonStates::LinearClamp() const
{
return DemandCreate(pImpl->linearClamp, [&](ID3D11SamplerState** pResult)
{
return pImpl->CreateSamplerState(D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, pResult);
});
}
ID3D11DepthStencilState* CommonStates::DepthRead() const
{
return DemandCreate(pImpl->depthRead, [&](ID3D11DepthStencilState** pResult)
{
return pImpl->CreateDepthStencilState(true, false, pResult);
});
}

可以看得出來,DemandCreate處理的流程相同,但是船進去的type跟Callback function不同,就能達到一般化的效果,這也是我目前看到最有趣的例子。