在MSVC10 下,將lambda expression 轉換成C 的function ...
文章推薦指數: 80 %
不過,在Visual C++ 2010(VC10)的環境下,其實lambda expression 有一些不符合標準的小問題…那就是他不能轉換成C 的function pointer(請 ...
直接觀看文章
OpenMenu
之前已經有在《C++0x:Lambdaexpression》一文中,介紹過C++11/C++0x這個算是滿好用的匿名函式、lambdaexpression了~透過lambdaexpression可以很快地建立一個functionobject,而不用另外宣告一個真正的函式,在很多地方,可以有效地簡化程式的寫法。
不過,在VisualC++2010(VC10)的環境下,其實lambdaexpression有一些不符合標準的小問題…那就是他不能轉換成C的functionpointer(請參考《在C++裡傳遞、儲存函式Part1:FunctionPointer》)、拿來註冊成callbackfunction。
下面是一個簡單的範例:
#include
解決方法的實作
那如果現在希望可以解決的話,要怎麼辦呢?在《FixingLambdaexpressionsinVisualStudio2010》這篇文章裡面,提供了一種透過Templatemetaprogramming的機制來做封包,藉此把lambdaexpression轉換為可以被VC10當作functionpointer的一般function。
他的方法的基本概念,就是透過根據傳入的lambdaexpressiob來產生一個特別的struct或class,然後透過裡面的staticmemberfunction和staticmemberdata來做操作。
而接下來的程式碼,就是Heresy根據文章中的方法,針對voidfunc()這種不需要參數、也沒有回傳值的函式,稍作修改後實作出來的結果。
首先,是最主要的templateclass:LambdaWrapper:
//theclasstowrapalambdaexpression
template
拿來當functionpointer/callbackfunction用的staticmemberfunctionExec()。
他所做的事就是去執行pFuncPtr這個指標所指到的lambdaexperession。
而由於有static的memberdatapFuncPtr,所以也需要globalscope產生他的實例、並進行初始化。
最後,則是Convert()這個直接輸入lambdaexpression、取得functionpointer的template函式了~
//definethetypeoffunctionpointer
typedef void(*FunctionType)();
//getfunctionpointerfromlambdaexpression
template
而在Convert()裡面的第一個動作,就是先建立一個static變數lf、將傳進來的lambdaexpressionrFunc複製一份;而由於lf是static的,所以會一直存在、不會在離開Convert()這個函式時被釋放掉。
(註1)
而接下來,則是去設定LambdaWrapper
如此一來,要使用的話就非常簡單了~只要下面這樣呼叫就可以了!
CallFunctionPointer(Convert([](){cout<::Exec()這個函式,然後執行我們所指定的lambdaexpression了~
所以這樣在把上面的程式寫好後,在使用上是非常方便的~像是glut這類本來需要globalfunction或staticmemberfunction的介面,都可以透過lambdaexpression來作封包,可以寫得更物件導向了~
不過,由於LambdaWrapper這樣的templateclass還是只能針對特定形式的functionpointer來做轉換,像這邊的例子就只能針對void()的形式,不能用在其他形式的functionpointer;所以如果要對應到不同類型的functionpointer,也就需要寫不同的類別出來了…這點也算是比較討厭的地方。
稍微詳細一點的解釋
這個方法主要是透過class的staticmemberdata來紀錄要執行的function、然後透過staticmemberfunction來當作呼叫的介面;但是class裡面的staticmemberdata基本上是共用的,這邊這樣設計,重複使用的時候難道不會在後面呼叫Convert()時被覆蓋掉嗎?(註2)
實際上,這個方法之所以可以這樣用,主要是因為編譯器在處理的時候,每一個lambdaexpression的型別都是不同的!下面就是一個簡單的測試例子:
#include
而也由於每一個lambdaexpression都是不同的,所以透過lambdaexpression來產生出來的templateclass:LambdaWrapper
而同樣的,Convert()這個template函式,也是針對不同的lambdaexpression、會在編譯階段、產生不同的function實體,而裡面用來複製、保存lambdaexpression的static變數lf,也都是不一樣的~
而如果這邊改用std::function這種functionobject的話,就會因為型別相同、而出現問題了~像下面的例子,就是同時展示使用lambdaexpression和functionobject的使用:
autofa=Convert([](){cout<fo1=[](){cout<fo2=[](){cout<形式的functionobjectfo1、fo2,然後再透過Convert()、產生對應的functionpointerf1、f2。
最後都準備就緒後,則是依序執行這些被產生出來的functionpointer。
而執行的結果,會是:
lambdaa
lambdab
functionobject1
functionobject1
可以發現,直接使用lambdaexpression的話,結果是正確、沒有問題的~但是如果是使用functionobject的物件的話,Convert()和LambdaWrapper都會因為丟進來的參數型別是相同的(都是function
如果在偵錯模式下實際下去看的話,也會發現其實f1和f2這兩個指標,實際上都是指到同一個位址,也就是LambdaWrapper
所以理所當然的,f1、f2的執行結果會是相同的,都是去呼叫到fo1這個functionobject的副本、輸出「functionobject1」的字樣。
附註:
在《FixingLambdaexpressionsinVisualStudio2010》這篇文章裡面的作法,是沒有去建立傳入的lambdaexpression、也就是rFunc的副本,而是讓pointer直接去指到這個在外部的lambdaexpression。
Heresy沒有這樣做、而是另外去建立一份lambdaexpression的副本的原因,是怕外部的lambdaexpression會隨著生命週期到了而消失,這時候可能在執行時會有問題。
FunctionTypeGen(inti)
{
autof=[i](){cout<
FunctionTypeConvert(TLambda&rFunc)
{
LambdaWrapper
不過實際上,LambdaWrapper
LambdaExpression只有在沒有capture任何變數的時候可以直接轉換成functionpointer,實際上這個時候它會被當成是類似globalfunction的形式;如果有做變數的capture的話,就變變成類似class的memberfucntion的形式、而無法轉換成上述的functionpointer。
例如下面的例子,在gcc和vc11上都會出現編譯錯誤:
#include
延伸文章資訊
- 112.7 — Introduction to lambdas (anonymous functions) - Learn C++
- 2Passing C++ captureless lambda as function pointer to C API
A lambda expression with an empty capture clause is convertible to a function pointer. It can rep...
- 3Passing C++ captureless lambda as function pointer to C API
- 4Behind C++ Lambda Functions - cpp - developer blog
But a lambda function isn't just a pointer. So what is it? Note, we're going to use Clang as an e...
- 5在MSVC10 下,將lambda expression 轉換成C 的function ...
不過,在Visual C++ 2010(VC10)的環境下,其實lambda expression 有一些不符合標準的小問題…那就是他不能轉換成C 的function pointer(請 ...