Passing C++ captureless lambda as function pointer to C API

文章推薦指數: 80 %
投票人數:10人

A lambda expression with an empty capture clause is convertible to a function pointer. It can replace a stand-alone or static member function as ... nextptr SignIn SignUp PassingC++capturelesslambdaasfunctionpointertoCAPI Alambdaexpressionwithanemptycaptureclauseisconvertibletoafunctionpointer.Itcanreplaceastand-aloneorstaticmemberfunctionasacallbackfunctionpointerargumenttoCAPI. Tutorial|Sep6,2019|jmiller  c++ lambda pthreads function-pointer c++11 Quiteoften,C++applicationsdependonthird-partyoroperatingsystemlibrariesthatareentirelywritteninCorexposeC-onlyinterface.SomeexamplesoftheseC-APIsaredatabasedrivers,messagingserviceAPIs,andnativethreadinginterfaces.Usually,theseC-APIstakefunctionpointerparametersforcallbackarguments.Let'staketheexampleofpthread_createfromthePOSIXpthreadlibrary. intpthread_create(pthread_t*thread,constpthread_attr_t*attr, void*(*start_routine)(void*),void*arg); Thepthread_createfunctioncreatesanewthreadthatstartsexecutionbyinvokingtheparameter,start_routine.Thelastparameter,arg,ispassedastheargumenttostart_routine. Suppose,wearecreatingaWorkerclassthatperiodicallydoessomeworkinabackgroundthread.TheWorker'sstartmethodcreatesanewthreadthroughpthread_create.ThenewbackgroundthreadalwaysexecutesinaloopintheWorker'srunmethoduntiltheloopisterminated.Thestopmethodsetsabooleanflagtoendtherunloopandjoinsthethread.BelowisthecodeoftheWorkerclass: //requirespthread.handunistd.h classWorker{ public: //Implementedlater intstart(); voidstop(){ stopped_=true; //waitforthethreadtoexitbyjoining pthread_join(threadId_,nullptr); } private: voidrun(){ while(!stopped_){ std::cout<void*{//capture-lesslambda static_cast(self)->run(); returnnullptr; }, this);//'this'asargumenttolambda } Thelambdaexpressionintheabovecodehasanemptycaptureclause([]),takesavoid*parameter,andhasanexplicitreturntypeofvoid*tomatchthestart_routinetype.ItcaststheselfparametertoWorker*andcallstherunmethodonit.Thelastparametertopthread_createisthethispointer,whichissubsequentlypassedtothelambdaexpressionasanargument.WecanusetheWorkerclassinthemainfunction,asshownbelow: intmain(){ Workerw; if(w.start()==0){ usleep(10000000);//wait10seconds w.stop(); }else{ std::cout<void*{//lambdacaptures'this' run(); returnnullptr; }, nullptr); } Thisbegsthequestion,whatmakescapture-lesslambdaexpressionssodifferentthattheycanbepassedasfunctionpointers?Theanswerliesinthedetailsofhowlambdaexpressionsareimplemented.Conceptually,alambdaexpressionissyntacticsugaronafunctionobject(aninstanceofaclasswithanoverloadedfunction-calloperator'()').Accordingtocppreference: Thelambdaexpressionisaprvalueexpressionofuniqueunnamednon-unionnon-aggregateclasstype,knownasclosuretype,whichis declared(forthepurposesofADL)inthesmallestblockscope,class scope,ornamespacescopethatcontainsthelambdaexpression. Forthesakeofsimplicity,thinkofalambdaexpressionasaninstanceofitsclasstypeknownasclosuretype,whichhasanoverloadedfunction-calloperator.Thevariablesinthelambda'scapture-listarestoredinthenonstaticdatamembersoftheclosuretype.Let'stakeanexampleofacapturinglambdaexpression: intx,y; autolambda=[x,y](intz){/*body*/}; Theclosuretypeoftheabovelambdacanbeconceptualizedas: structClosure{ //....constructors voidoperator()(intz)const{/*body*/} intx,y; }; Thefunction-calloperatorisconstunlessthelambdaisdeclaredmutable.Acapture-lesslambdaalsohasasimilarclosuretypeexceptthattherearenononstaticdatamembersinit.However,acapture-lesslambdahasanadditionalfunction-pointerconversionoperatordeclaredinit.Itisthatfunction-pointerconversionoperatorthatmakesacapture-lesslambdaconvertibletoafunctionpointer.Thecppreferencedescribesitas: Thisuser-definedconversionfunctionisonlydefinedifthecapture listofthelambda-expressionisempty.Itisapublic,constexpr, (sinceC++17)non-virtual,non-explicit,constnoexcept(sinceC++14) memberfunctionoftheclosureobject. Therefore,theclosuretypeofacapture-lesslambdathattakesonlyonevoid*parameterandreturnsvoid*couldbeimaginedas: structCaptureLessClosure{ //....constructors //function-calloperator void*operator()(void*arg)const{ returnimpl(arg); } //createafunctionpointeralias usingF=void*(*)(void*); //conversionoperatortothefunctionpointer constexproperatorF()const{returnimpl;} //implementationoflambdabodyinastaticmethod staticvoid*impl(void*arg){ /*body(includingreturn)*/ } }; So,whyonlyacapture-lessclosuretypehasafunctionpointerconversionoperator?Notethat,thebodyofthelambdaismovedtoastaticmemberfunctionandthestaticfunction'saddressisreturnedfromtheconversionoperator.Thatisoneofthepossibleimplementationsofthecapture-lessclosuretype.Ihaveonlymadeareasonableassumptionbasedonthefactthatbecauseacapture-lessclosuretypehasnononstaticdatamembers,thebodyofthelambdacanbeimplementedasastaticmemberfunction,whichmakesitpossibletoconvertthecapture-lesslambdatoafunctionpointer. Inshort,capture-lesslambdaexpressionsareconvertibletofunctionpointersandcanbeusedasfunctionpointerargumentsinplaceofstand-aloneorstaticmemberfunctions. Formoreonlambdaexpressions: Lambdaexpressions:cppreference LambdaexpressionsandclosuresforC++:BjarneStroustrup



請為這篇文章評分?