Behind C++ Lambda Functions - cpp - developer blog

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

But a lambda function isn't just a pointer. So what is it? Note, we're going to use Clang as an example. A lot of things in C++ are up to compiler dev's ... blog project developerblog Menu BehindC++LambdaFunctions source 20737wordsclangcppcpp Lambdafunctionsarenotjustpointerstoafunction.Thisisobviouswhenyouthinkaboutit:ifthey'reanonymous,anddeclaredcompletelydifferently,somethingmustbedifferent.But,wecanassignapointertoafunctiontoalambdaeasily: void(*ptr)()=[](){}; Sohowdoesthatwork?Weknowwe'rejustassigningtoapointer.Butalambdafunctionisn'tjustapointer.Sowhatisit? Note,we'regoingtouseClangasanexample.AlotofthingsinC++areuptocompilerdev'simagination,alsoknownasimplementationspecific.SoifIsay"lambdashave12toes,"thatisbecauseClangdefineslambdasashaving12toes,notbecausetheyhavetohave12toes. Lambdasareclasses Whenyoudefinealambdafunction,you'rereallymakingaC++class.Thisclassimplementsthemethodoperator().Whenyoucallthelambda,you'rejustcallingthatoperatorimplementation. Here'sasimpleC++programtoshowthat: intmain(){ [](){}(); } Thatlineinmainisjustdefiningalambdawithnocaptures([]),thattakesnoargument(()),withnobody({}),thencallingit(()).WecanactuallylookatwhatClangdevelopsastheASTwith: clang++-Xclang-ast-dumplambda.cpp We'llwalkthroughthelambdafunctionpiecebypiece. LookingattheAST TheASTisjusttheinternalrepresentationofthecodethatthecompilerlooksat.Let'slookatthemostobviouspartoftheLambdashenanigans(note,obviousisrelative): `-CXXOperatorCallExpr0x17238c8<3>'void':'void' |-ImplicitCastExpr0x1723858<9>'void(*)()const' |`-DeclRefExpr0x17237d8<9>'void()const'lvalueCXXMethod0x1723190'operator()''void()const' `-ImplicitCastExpr0x17238b0<3>'const(lambdaatlambda.cpp:2:3)'lvalue `-MaterializeTemporaryExpr0x1723898<3>'(lambdaatlambda.cpp:2:3)'lvalue `-LambdaExpr0x17236a0<3>'(lambdaatlambda.cpp:2:3)' Alright.Whatwehavehereisacallexpression-that'sthelast()inourlambda.Then,wehavesomegibberishthatisClangdoinginternalconversionsinordertomakethetypesright.Finally,wehaveourtrustedLambdaExpr-ourlambda!Let'slookatwhat'sinthat. `-LambdaExpr0x17236a0<3>'(lambdaatlambda.cpp:2:3)' |-CXXRecordDecl0x1723050<3>col:3implicitclassdefinition ||-DefinitionDatalambdapass_in_registersemptystandard_layouttrivially_copyablecan_const_default_init |||-DefaultConstructordefaulted_is_constexpr |||-CopyConstructorsimpletrivialhas_const_paramneeds_implicitimplicit_has_const_param |||-MoveConstructorexistssimpletrivialneeds_implicit |||-CopyAssignmenttrivialhas_const_paramneeds_implicitimplicit_has_const_param |||-MoveAssignment ||`-Destructorsimpleirrelevanttrivial ||-CXXMethodDecl0x1723190<6>col:3usedoperator()'void()const'inline ||`-CompoundStmt0x1723240<7> ||-CXXConversionDecl0x1723538<3>col:3implicitoperatorvoid(*)()'void(*()constnoexcept)()'inline ||-CXXMethodDecl0x17235e8<3>col:3implicit__invoke'void()'staticinline |`-CXXDestructorDecl0x17236d0<3>col:3implicitreferenced~'void()noexcept'inlinedefaulttrivial `-CompoundStmt0x1723240<7> Oh,gosh.We'llgopiecebypiecethroughthis,buthere'sourclass.ACXXRecordDeclinClangrepresentsaC++struct,union,orclass.Soourlambdaactuallycontainsthatclassinitsnode. WecanevenseethatinClangdocumentation.IfwegotothedocumentationforLambdaExprinClang,wecanactuallyseeabunchofusefulfunctions.Notably,wecanseegetLambdaClass(),whichwill"retrievetheclassthatcorrespondstothelambda."Neat! We'llskiptheDefinitionDatapartoftheclass,sincethat'sjustdefiningsomedefaultconstructorsandwhatnot.Let'slookattheoperator()definition: ||-CXXMethodDecl0x1723190<6>col:3usedoperator()'void()const'inline ||`-CompoundStmt0x1723240<7> Herewehaveourcalloperator.It'sempty,andreturnsvoid,soit'snotlikeweexpecttoseemuch.But,let'smakeithavesomething.Ifwechangeourlambdafunctionto: [](){inti=1;}(); Wenowhavesomethinginourfunctionbody,soweexpecttoseeitinthecalloperator: ||-CXXMethodDecl0x171f190<6>col:3usedoperator()'void()const'inline ||`-CompoundStmt0x171f2f8<7> ||`-DeclStmt0x171f2e0<8> ||`-VarDecl0x171f258<8>col:12i'int'cinit ||`-IntegerLiteral0x171f2c0<16>'int'1 Andtherewehaveit,ourcalloperatorhasabody,justnestedintheAST.Howaboutareturntype? [](){return1;}(); IntheAST: ||-CXXMethodDecl0x1f88190<6>col:3usedoperator()'int()const'inline ||`-CompoundStmt0x1f883b8<7> ||`-ReturnStmt0x1f883a8<8> ||`-IntegerLiteral0x1f88240<15>'int'1 Aswecansee,ourCXXMethodDeclchangedfromvoid()consttoint()const,showingourchangeinreturntype! Finally,howaboutaddingaparameter? [](inti){}(1); AST: ||-CXXMethodDecl0xe84220<11>col:3usedoperator()'void(int)const'inline |||-ParmVarDecl0xe83fc8<6>col:10i'int' ||`-CompoundStmt0xe842d8<12> WecanseethattheCXXMethodDeclisnowvoid(int)constandwehaveaParmVarDecl(justafunctionparameter)insideofthemethoddecl.Awesome. But,lambdasalsoletyoucaptureavariable.ThismeansthatifIhaveavariableioutsideofthelambda,captureit,changeit,thencallthelambda,weshouldseethatvariablechange.Let'stryitout: #include intmain(){ inti; autolambda=[&i](){ std::cout<col:5implicit'int&' Wehaveafieldinsideofourlambda'sclassnow!It'sareferencetoanint,namelytoideclaredoutside.Then,whenweuseourlambda,wejustusethatfield!Neat. Now,wedon'ttakeaparameter,westillreturnvoid,canweassignthistoaCfunctionpointerlikewedidatthebeginning? intmain(){ inti; void(*ptr)()=[&i](){}; } Thecompilererrors! lambda.cpp:5:10:error:noviableconversionfrom'(lambdaatlambda.cpp:5:19)'to'void(*)()' void(*ptr)()=[&i](){}; ^~~~~~~~~ It'sweirdbecausewestillhaveallthequalificationsofthatfunctionpointer.But,ifwelookattheAST,we'llseesomethingmissingfromtheLambda'sclass,particularly: ||-CXXConversionDecl0x142f7b8<19>col:19implicitusedoperatorvoid(*)()'void(*()constnoexcept)()'inline ||`-CompoundStmt0x142fba0<19> ||`-ReturnStmt0x142fb90<19> ||`-ImplicitCastExpr0x142fb78<19>'void(*)()' ||`-DeclRefExpr0x142fb58<19>'void()'lvalueCXXMethod0x142f868'__invoke''void()' Clangactuallyrefusestogenerateaconversiontothefunctionpointerifwehaveanycaptures!Thatmakessensebecausethefunctionnowreliesonthatvariablei. Let'ssaywe'rereallycuriousandneedtoknowwhythishappened.Howcanwedothat? DivingintoClang'scode Luckily,Clang'ssourcecodeisopensource.Youcanfindit,alongwithLLVMandmore,onGithub.Furthermore,ifyouwanttoknowsomethinghighlevelaboutClang'scode,checkouttheClanginternalsmanual. But,ourjobissimple:findoutwhereitrefusestogeneratetheconversionfunction. Thefirstplacetostartrequiresabitofcompilerknowledge.ClanghasaSemalibraryforcertainsemanticactions.ThisSemalibraryiswhatbuildstheAST.Consideringthat,weprobablyhaveagoodplacetostart,sincewenoticedadifferenceintheAST. Atthistime,youcanfindtheSemalibraryontheGithubunderllvm-project/clang/lib/Sema/ Nowthatwe'rehere,wewanttoseewhatcreatesthelambdafunction.Asolidwaytodothiswouldbetolookuptheconstructorinthedocumentationandseewhatcallsitandwhen.But,ourcaseislucky:there'safilecalledSemaLambda.cpp,solet'scheckthatout. Ifwedigaroundhere,wefindthefunctionBuildLambdaExpr.Seemspromising. Fromthere,let'sjustlookthroughthefunction. Firstwedosomesetupfrompreviouslygatheredinfo: CallOperator=LSI->CallOperator; Class=LSI->Lambda; IntroducerRange=LSI->IntroducerRange; ExplicitParams=LSI->ExplicitParams; ExplicitResultType=!LSI->HasImplicitReturnType; LambdaCleanup=LSI->Cleanup; ContainsUnexpandedParameterPack=LSI->ContainsUnexpandedParameterPack; IsGenericLambda=Class->isGenericLambda(); Then,wedosomelogiccheckingifcapturesareusedandgettingtheminagoodformforourAST. Next,wecanseewhywedon'tgetourconversionfunction: if(Captures.empty()&&CaptureDefault==LCD_None) addFunctionPointerConversions(*this,IntroducerRange,Class, CallOperator); So,weonlyaddthefunctionpointerconversionifwehavenocapturesandthecapturedefaultisnone! Ifyoureallywanttodiveintotheweeds,theClangdevsalsooftencommentthepartofthestandardtheytookfrom: //C++11[expr.prim.lambda]p6: //Theclosuretypeforalambda-expressionwithnolambda-capture //hasapublicnon-virtualnon-explicitconstconversionfunction //topointertofunctionhavingthesameparameterandreturn //typesastheclosuretype'sfunctioncalloperator. Youcanfindthisyourselftoo!JustlookaroundforC++standards(likethroughopen-std.orgorjustasearchengine)andfindthesectionwithexpr.prim.lambda.You'llseenumbering,findnumber6,andyou'llfindidenticalorsimilarwordingtothat. Wasthisaboutlambdas? Notreally.LambdaswerejustamechanismforshowinghowtofindoutmoreaboutC++bydiggingintoit.Thisappliestoanylanguageorcompilertoo,especiallyiftheyletyoudumpast(orsomeintermediaterepresentation).It'salsojustfuntolookatcomplexcode. Sothereyougo.Trythisoutthenexttimeyouseeaweirdconstructinyourfavoritelanguage.Sometimes,you'llgetsurprisedandfallintoafunrabbithole.It'swortheveryhour. Project:cpp Aboutthistopic(BehindC++LambdaFunctions),Wefoundmoreinformationhere,checkoutthelink https://dev.to/evantypanski/behind-c-lambda-functions-2169 FOCUSONFINDINGGREATDEVELOPERCONTENT (CollectionandSharebasedontheCCProtocol.) LeetCode189.RotateArray(javascriptsolution) TypingeffectinReactwithtyped.jsandhooks RelatedPosts Directiveslikeng-showdonotwork Amnotyetsureifthisisbecauseofaproblemwithmy$parseimplementation,orwhetherwewillneedtoimplementclang-show,etc. ActuallyIbelievetheystillworkwithregulardataonthescope,justnotwithclojur... Ininterpolationsonlyjs-stylefunctionswork Theclojurestyleoneshouldworkbutappearnottolookupthefunctioncorrectly. oopsIalreadycreatedthisone... Textinputlosesfocusoneverykeystroke Thisappearstohappenbecausethechangeisbeingappliedtothecollectionwhichcausesittore-render. Theupshotisthatyouneedtohavemutabilityavailabletoyouforpropertiesthatneedtostayfocussedwhilet... clangseemsnotsuchagoodprojectname clangisalreadybeingusedforcompilingc/c++,soyourprojectmightfinditdifficulttobefoundhttp://clang.llvm.org/ Hey,yeahI'mawareoftheotherproject,butIdon'tthinkthere'sanychanceofconfusion. If... can'trequire`clang.js-types` WhenIrequireclang.js-typesinoneofmy.cljsfile,leincljsbuildautogivesthefollowingwarnings: WARNING:UseofundeclaredVarclang.js-types/INamedatline28/Users/xhh/builds/clang/client/clang/js_types.cljs... Brokenonlein-cljsbuild0.3.2 Buildingaprojectwithclang-0.1.0-SNAPSHOT,lein-cljsbuild0.3.2andclojurescript-0.0-1803causesthefollowingerror: Notsureaboutthesourceoftheproblem,willseeifwecangetthisfixedup. Inthemeantime,p... scope!appearsinthereadmebutnotinthecode Idecidedtoremovescope! in7a9ce9d0229dcb33451b0d663368c6394943dc1cwhenIextendedthebuilt-inJSdatatypeswithgoodsupportforclojure'sstandardprotocols. Ithinkthisisbettersincethescope! Settingavalu... IsClangstable? Hello, Ihavetwoquestions:-Clangisitreadytodevelopproductiontools? -WhenwillclangpushonClojar? No,youshouldn'tconsideritproductionreadyunlessyouarewillingtodiveinandfixorextendclang... "clang.todo"and["clang"]meaning Isitpossibletoexplainwhatarethesemanticsof"clang.todo"and["clang"]inthecontextofyourprojectsample? OKIgotthemeaningbyopeninghttps://github.com/pangloss/clang/blob/master/resources/public/index.h... HigherlevelthreadinginC++ Herethestd::condition_variablecomestotherescue,thisissynchronizationprimitivethatcanbeusedtoblockthreads,untilanotherthreadmodifiesthewaitconditionandnotifiesthewaitingthread. notify:Thisfu... 🍪Thiswebsiteusescookiestoensureyougetthebestexperienceonourwebsite. Learnmore IGotIt



請為這篇文章評分?