C++ Rusland: hoe dit gebeur het

As jy aan die begin van die toneelstuk sê dat daar C++-kode aan die muur hang, sal dit jou teen die einde sekerlik in die voet skiet.

Bjarne Stroustrup

Van 31 Oktober tot 1 November is die C++ Russia Piter-konferensie in St. Petersburg gehou - een van die grootskaalse programmeringskonferensies in Rusland, gereël deur JUG Ru Group. Genooide sprekers sluit in lede van die C++-standaardkomitee, CppCon-sprekers, O'Reilly-boekskrywers en instandhouers van projekte soos LLVM, libc++ en Boost. Die konferensie is gemik op ervare C++-ontwikkelaars wat hul kundigheid wil verdiep en ervarings in lewendige kommunikasie wil uitruil. Studente, gegradueerde studente en universiteitsonderwysers kry baie goeie afslag.

Die Moskou-uitgawe van die konferensie sal so vroeg as April volgende jaar beskikbaar wees om te besoek, maar intussen sal ons studente jou vertel watter interessante dinge hulle by die laaste geleentheid geleer het. 

C++ Rusland: hoe dit gebeur het

Foto van konferensie album

Meer oor ons

Twee studente van die Nasionale Navorsingsuniversiteit Hoër Skool vir Ekonomie - St. Petersburg het aan hierdie pos gewerk:

  • Liza Vasilenko is 'n 4de jaar voorgraadse student wat Programmeringstale studeer as deel van die Toegepaste Wiskunde en Rekenaarwetenskap-program. Nadat ek in my eerste jaar op universiteit met die C++-taal kennis gemaak het, het ek daarna ondervinding opgedoen om daarmee te werk deur internskappe in die bedryf. My passie vir programmeertale in die algemeen en funksionele programmering in die besonder het sy stempel afgedruk op die keuse van verslae by die konferensie.
  • Danya Smirnov is 'n 1ste jaar student van die meestersgraadprogram "Programmering en Data-analise". Terwyl ek nog op skool was, het ek Olimpiade-probleme in C++ geskryf, en toe gebeur dit op een of ander manier dat die taal voortdurend in opvoedkundige aktiwiteite ter sprake gekom het en uiteindelik die hoofwerktaal geword het. Ek het besluit om aan die konferensie deel te neem om my kennis te verbeter en ook van nuwe geleenthede te leer.

In die nuusbrief deel die fakulteitsleierskap dikwels inligting oor opvoedkundige geleenthede wat met ons spesialiteit verband hou. In September het ons inligting oor C++ Rusland gesien en besluit om as luisteraars te registreer. Dit is ons eerste ervaring van deelname aan sulke konferensies.

Konferensie struktuur

  • Berigte

In die loop van twee dae het kundiges 30 verslae gelees wat baie warm onderwerpe dek: vernuftige gebruike van taalkenmerke om toegepaste probleme op te los, komende taalopdaterings in verband met die nuwe standaard, kompromieë in C++-ontwerp en voorsorgmaatreëls wanneer daar met die gevolge daarvan gewerk word, voorbeelde interessante projekargitektuur, sowel as 'n paar onder-die-kap-besonderhede van die taalinfrastruktuur. Drie optredes het gelyktydig plaasgevind, meestal twee in Russies en een in Engels.

  • Besprekingsones

Na die toespraak is alle ongevraagde vrae en onvoltooide besprekings oorgeplaas na spesiaal aangewese areas vir kommunikasie met die sprekers, toegerus met nasienborde. 'n Goeie manier om die pouse tussen toesprake te verbreek met 'n aangename gesprek.

  • Weerligpraatjies en informele gesprekke

As jy 'n kort verslag wil gee, kan jy op die witbord inskryf vir die aand Weerligpraatjie en vyf minute tyd kry om oor enigiets oor die konferensie-onderwerp te praat. Byvoorbeeld, 'n vinnige inleiding tot ontsmettingsmiddels vir C++ (vir sommige was dit nuut) of 'n storie oor 'n fout in sinusgolfgenerering wat net gehoor, maar nie gesien kan word nie.

Nog 'n formaat is die paneelbespreking "Met 'n hart tot hart-komitee." Op die verhoog is 'n paar lede van die standaardiseringskomitee, op die projektor is 'n kaggel (amptelik - om 'n opregte atmosfeer te skep, maar die rede "omdat ALLES AAN VUUR" lyk snaakser), vrae oor die standaard en die algemene visie van C++ , sonder hewige tegniese besprekings en holiwars. Dit het geblyk dat die komitee ook lewende mense bevat wat dalk nie heeltemal seker is van iets nie of dalk iets nie weet nie.

Vir aanhangers van holivars, het die derde geleentheid op die saak gebly - die BOF-sessie "Go vs. C++". Ons neem 'n Go-liefhebber, 'n C++-liefhebber, voor die aanvang van die sessie berei hulle saam 100500 XNUMX skyfies oor 'n onderwerp voor (soos probleme met pakkette in C++ of die gebrek aan generiese in Go), en dan voer hulle 'n lewendige bespreking onder mekaar en met die gehoor, en die gehoor probeer om twee standpunte gelyktydig te verstaan. As 'n holivar buite konteks begin, gryp die moderator in en versoen die partye. Hierdie formaat is verslawend: etlike ure na die begin is slegs die helfte van die skyfies voltooi. Die einde moes baie versnel word.

  • Vennoot staan

Die vennote van die konferensie was in die sale verteenwoordig – by die stalletjies het hulle oor huidige projekte gesels, internskappe en indiensneming aangebied, vasvrae en klein kompetisies gehou en ook lekker pryse uitgeloot. Terselfdertyd het sommige maatskappye selfs aangebied om deur die aanvanklike stadiums van onderhoude te gaan, wat nuttig kan wees vir diegene wat nie net na verslae kom luister het nie.

Tegniese besonderhede van die verslae

Ons het albei dae na berigte geluister. Dit was soms moeilik om een ​​verslag uit die parallelle te kies – ons het ooreengekom om uitmekaar te gaan en die kennis wat tydens pouses opgedoen is, uit te ruil. En tog blyk dit dat baie uitgelaat word. Hier wil ons praat oor die inhoud van sommige van die verslae wat ons die interessantste gevind het

Uitsonderings in C++ deur die prisma van samestelleroptimalisasies, Roman Rusyaev

C++ Rusland: hoe dit gebeur het
Skuif van aanbiedings

Soos die titel aandui, het Roman gekyk na werk met uitsonderings deur LLVM as voorbeeld te gebruik. Terselfdertyd, vir diegene wat nie Clang in hul werk gebruik nie, kan die verslag steeds 'n idee gee van hoe die kode moontlik geoptimaliseer kan word. Dit is so omdat die ontwikkelaars van samestellers en ooreenstemmende standaardbiblioteke met mekaar kommunikeer en baie suksesvolle oplossings kan saamval.

Dus, om 'n uitsondering te hanteer, moet jy baie dinge doen: bel die hanteringskode (indien enige) of gratis hulpbronne op die huidige vlak en draai die stapel hoër op. Dit alles lei tot die feit dat die samesteller bykomende instruksies byvoeg vir oproepe wat moontlik uitsonderings gee. Daarom, as die uitsondering nie werklik geopper word nie, sal die program steeds onnodige aksies uitvoer. Om op een of ander manier bokoste te verminder, het LLVM verskeie heuristieke om situasies te bepaal waar uitsonderingshanteringskode nie bygevoeg hoef te word nie of die aantal "ekstra" instruksies verminder kan word.

Die spreker ondersoek omtrent 'n dosyn daarvan en wys beide situasies waar dit help om die uitvoering van die program te bespoedig, en dié waar hierdie metodes nie van toepassing is nie.

Dus, Roman Rusyaev lei studente tot die gevolgtrekking dat kode wat uitsonderingshantering bevat, nie altyd uitgevoer kan word met nul bokoste nie, en gee die volgende raad:

  • wanneer biblioteke ontwikkel word, is dit die moeite werd om uitsonderings in beginsel te laat vaar;
  • as uitsonderings steeds nodig is, is dit waar moontlik die moeite werd om noexcept (en const) wysigers oral by te voeg sodat die samesteller soveel as moontlik kan optimeer.

Oor die algemeen het die spreker die siening bevestig dat uitsonderings die beste tot 'n minimum gebruik word of heeltemal laat vaar word.

Die verslagskyfies is beskikbaar by die volgende skakel: [“C++-uitsonderings deur die lens van LLVM-samestelleroptimalisasies”]

Kragopwekkers, koroutines en ander soetheid wat die brein ontrol, Adi Shavit

C++ Rusland: hoe dit gebeur het
Skuif van aanbiedings

Een van die vele verslae by hierdie konferensie gewy aan innovasies in C++20 was onvergeetlik, nie net vir sy kleurvolle aanbieding nie, maar ook vir sy duidelike identifikasie van bestaande probleme met die versamelingsverwerkingslogika (vir lus, terugbel).

Adi Shavit beklemtoon die volgende: die tans beskikbare metodes gaan deur die hele versameling en bied nie toegang tot een of ander interne tussentoestand nie (of dit doen in die geval van terugbelopings, maar met 'n groot aantal onaangename newe-effekte, soos Terugbelhel) . Dit wil voorkom asof daar iterators is, maar selfs met hulle is alles nie so glad nie: daar is geen algemene toegangs- en uitgangpunte nie (begin → einde versus rbegin → rend ensovoorts), dit is nie duidelik hoe lank ons ​​sal herhaal nie? Begin met C++20, hierdie probleme is opgelos!

Eerste opsie: reekse. Deur iterators te verpak, kry ons 'n gemeenskaplike koppelvlak vir die begin en einde van 'n iterasie, en ons kry ook die vermoë om te komponeer. Dit alles maak dit maklik om volwaardige dataverwerkingspyplyne te bou. Maar nie alles is so glad nie: 'n deel van die berekeningslogika is binne die implementering van 'n spesifieke iterator geleë, wat die kode kan bemoeilik om te verstaan ​​en te ontfout.

C++ Rusland: hoe dit gebeur het
Skuif van aanbiedings

Wel, vir hierdie geval het C++20 koroutines bygevoeg (funksies waarvan die gedrag soortgelyk is aan kragopwekkers in Python): uitvoering kan uitgestel word deur 'n mate van huidige waarde terug te gee terwyl 'n tussentoestand behoue ​​bly. Ons bereik dus om nie net met data te werk soos dit voorkom nie, maar ook om al die logika binne 'n spesifieke koroutine in te kap.

Maar daar is 'n vlieg in die salf: op die oomblik word hulle net gedeeltelik ondersteun deur bestaande samestellers, en word ook nie so netjies geïmplementeer as wat ons wil nie: dit is byvoorbeeld nog nie die moeite werd om verwysings en tydelike voorwerpe in koroutines te gebruik nie. Boonop is daar 'n paar beperkings op wat koroutines kan wees, en constexpr-funksies, konstrukteurs / vernietigers en hoof is nie by hierdie lys ingesluit nie.

Coroutines los dus 'n beduidende deel van die probleme op met die eenvoud van dataverwerkingslogika, maar hul huidige implementering vereis verbetering.

materiaal:

C++ truuks van Yandex.Taxi, Anton Polukhin

In my professionele aktiwiteite moet ek soms suiwer bykomstighede implementeer: 'n omhulsel tussen die interne koppelvlak en die API van een of ander biblioteek, aanteken of ontleed. In hierdie geval is daar gewoonlik geen behoefte aan enige bykomende optimalisering nie. Maar wat as hierdie komponente in sommige van die gewildste dienste op die RuNet gebruik word? In so 'n situasie sal jy alleen teragrepe per uur se logs moet verwerk! Dan tel elke millisekonde en daarom moet jy jou tot verskeie truuks wend – Anton Polukhin het daaroor gepraat.

Miskien was die interessantste voorbeeld die implementering van die pointer-to-implementation (puisie) patroon. 

#include <third_party/json.hpp> //PROBLEMS! 
struct Value { 
    Value() = default; 
    Value(Value&& other) = default; 
    Value& operator=(Value&& other) = default; 
    ~Value() = default; 

    std::size_t Size() const { return data_.size(); } 

private: 
    third_party::Json data_; 
};

In hierdie voorbeeld wil ek eers ontslae raak van die koplêers van eksterne biblioteke - dit sal vinniger saamstel, en jy kan jouself beskerm teen moontlike naamkonflikte en ander soortgelyke foute. 

Goed, ons het #include na die .cpp-lêer geskuif: ons benodig 'n voorwaartse verklaring van die toegedraaide API, sowel as std::unique_ptr. Nou het ons dinamiese toekennings en ander onaangename dinge soos data wat oor 'n klomp data versprei is en verminderde waarborge. std::aligned_storage kan met dit alles help. 

struct Value { 
// ... 
private: 
    using JsonNative = third_party::Json; 
    const JsonNative* Ptr() const noexcept; 
    JsonNative* Ptr() noexcept; 

    constexpr std::size_t kImplSize = 32; 
    constexpr std::size_t kImplAlign = 8; 
    std::aligned_storage_t<kImplSize, kImplAlign> data_; 
};

Die enigste probleem: jy moet die grootte en belyning vir elke omhulsel spesifiseer - kom ons maak ons ​​puisiesjabloon met parameters , gebruik 'n paar arbitrêre waardes en voeg 'n tjek by die vernietiger dat ons alles reggekry het: 

~FastPimpl() noexcept { 
    validate<sizeof(T), alignof(T)>(); 
    Ptr()->~T(); 
}

template <std::size_t ActualSize, std::size_t ActualAlignment>
static void validate() noexcept { 
    static_assert(
        Size == ActualSize, 
        "Size and sizeof(T) mismatch"
    ); 
    static_assert(
        Alignment == ActualAlignment, 
        "Alignment and alignof(T) mismatch"
    ); 
}

Aangesien T reeds gedefinieer is tydens die verwerking van die vernietiger, sal hierdie kode korrek ontleed word en in die samestellingstadium sal dit die vereiste grootte en belyningswaardes uitvoer wat as foute ingevoer moet word. Dus, ten koste van een bykomende samestellingslopie, raak ons ​​ontslae van die dinamiese toekenning van toegedraaide klasse, versteek die API in 'n .cpp-lêer met die implementering, en kry ook 'n ontwerp wat meer geskik is vir kas deur die verwerker.

Logging en ontleding het minder indrukwekkend gelyk en sal daarom nie in hierdie resensie genoem word nie.

Die verslagskyfies is beskikbaar by die volgende skakel: ["C++ truuks van Taxi"]

Moderne tegnieke om jou kode DROOG te hou, Björn Fahller

In hierdie praatjie wys Björn Fahller verskeie maniere om die stilistiese fout van herhaalde toestandskontroles te bekamp:

assert(a == IDLE || a == CONNECTED || a == DISCONNECTED);

Klink dit bekend? Deur verskeie kragtige C++-tegnieke te gebruik wat in onlangse standaarde bekendgestel is, kan jy dieselfde funksionaliteit elegant implementeer sonder enige prestasieboete. Vergelyk:   

assert(a == any_of(IDLE, CONNECTED, DISCONNECTED));

Om 'n onvaste aantal tjeks te hanteer, moet jy onmiddellik variadiese sjablone en vou-uitdrukkings gebruik. Kom ons neem aan dat ons die gelykheid van verskeie veranderlikes aan die enum se state_type element wil kontroleer. Die eerste ding wat by jou opkom, is om 'n helperfunksie te skryf is_enige_van:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

template <typename ... Ts>
bool is_any_of(state_type s, const Ts& ... ts) { 
    return ((s == ts) || ...); 
}

Hierdie tussenuitslag is teleurstellend. Tot dusver word die kode nie meer leesbaar nie:

assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED)); 

Nie-tipe sjabloonparameters sal help om die situasie 'n bietjie te verbeter. Met hul hulp sal ons die optelbare elemente van die enum na die lys van sjabloonparameters oordra: 

template <state_type ... states>
bool is_any_of(state_type t) { 
    return ((t == states) | ...); 
}
	
assert(is_any_of<IDLE, DISCONNECTING, DISCONNECTED>(state)); 

Deur outo in 'n nie-tipe sjabloonparameter (C++17) te gebruik, veralgemeen die benadering eenvoudig na vergelykings nie net met state_type-elemente nie, maar ook met primitiewe tipes wat as nie-tipe sjabloonparameters gebruik kan word:


template <auto ... alternatives, typename T>
bool is_any_of(const T& t) {
    return ((t == alternatives) | ...);
}

Deur hierdie opeenvolgende verbeterings word die verlangde vlot sintaksis vir tjeks bereik:


template <class ... Ts>
struct any_of : private std::tuple<Ts ...> { 
// поленимся и унаследуем конструкторы от tuple 
        using std::tuple<Ts ...>::tuple;
        template <typename T>
        bool operator ==(const T& t) const {
                return std::apply(
                        [&t](const auto& ... ts) {
                                return ((ts == t) || ...);
                        },
                        static_cast<const std::tuple<Ts ...>&>(*this));
        }
};

template <class ... Ts>
any_of(Ts ...) -> any_of<Ts ... >;
 
assert(any_of(IDLE, DISCONNECTING, DISCONNECTED) == state);

In hierdie voorbeeld dien die afleidingsgids om die gewenste struktuursjabloonparameters aan die samesteller voor te stel, wat die tipes van die konstruktorargumente ken. 

Verder - meer interessant. Bjorn leer hoe om die resulterende kode te veralgemeen vir vergelykingsoperateurs verder as ==, en dan vir arbitrêre bewerkings. Langs die pad word sulke kenmerke soos geen_unieke_adres-kenmerk (C++20) en sjabloonparameters in lambda-funksies (C++20) verduidelik deur gebruik te maak van voorbeelde van gebruik. (Ja, nou is die lambda-sintaksis selfs makliker om te onthou - dit is vier opeenvolgende pare hakies van alle soorte.) Die finale oplossing deur funksies as konstruktorbesonderhede te gebruik maak my siel warm, om nie eens te praat van die uitdrukking tuple in die beste tradisies van lambda calculus.

Aan die einde, moenie vergeet om dit op te poets nie:

  • Onthou dat lambdas gratis is constexpr; 
  • Kom ons voeg perfekte aanstuur by en kyk na sy lelike sintaksis in verhouding tot die parameterpakket in die lambda-sluiting;
  • Kom ons gee die samesteller meer geleenthede vir optimalisering met voorwaardelike noexcept; 
  • Kom ons sorg vir meer verstaanbare foutuitvoer in sjablone danksy eksplisiete terugkeerwaardes van lambdas. Dit sal die samesteller dwing om meer kontrole te doen voordat die sjabloonfunksie eintlik opgeroep word - by die tipe kontrolering stadium. 

Vir besonderhede, verwys asseblief na die lesingmateriaal: 

Ons indrukke

Ons eerste deelname aan C++ Rusland was onvergeetlik vir sy intensiteit. Ek het die indruk gekry van C++ Rusland as 'n opregte gebeurtenis, waar die lyn tussen opleiding en lewendige kommunikasie amper onmerkbaar is. Alles, van die stemming van die sprekers tot die kompetisies van die geleentheidsvennote, is bevorderlik vir hewige besprekings. Die inhoud van die konferensie, wat uit verslae bestaan, dek 'n redelike wye reeks onderwerpe, insluitend C++-innovasies, gevallestudies van groot projekte en ideologiese argitektoniese oorwegings. Maar dit sal onregverdig wees om die sosiale komponent van die gebeurtenis te ignoreer, wat help om taalhindernisse te oorkom in verband met nie net C++ nie.

Ons bedank die konferensie-organiseerders vir die geleentheid om aan so 'n geleentheid deel te neem!
Jy het dalk die organiseerders se plasing oor die verlede, hede en toekoms van C++ Rusland gesien op die JUG Ru-blog.

Dankie dat jy gelees het, en ons hoop dat ons oorvertelling van gebeure nuttig was!

Bron: will.com

Voeg 'n opmerking