C++ Venemaa: kuidas see juhtus

Kui lavastuse alguses ütled, et seinal ripub C++ kood, siis lõpuks lööb see sulle kindlasti jalga.

Bjarne Stroustrup

31. oktoobrist 1. novembrini toimus Peterburis C++ Russia Piter konverents – üks suuremahulisi programmeerimiskonverentse Venemaal, mida korraldas JUG Ru Group. Kutsutud esinejate hulka kuuluvad C++ standardikomitee liikmed, CppConi esinejad, O'Reilly raamatute autorid ja selliste projektide hooldajad nagu LLVM, libc++ ja Boost. Konverents on suunatud kogenud C++ arendajatele, kes soovivad süvendada oma teadmisi ja vahetada kogemusi otsesuhtluses. Üliõpilastele, magistrantidele ja ülikooli õppejõududele tehakse väga häid allahindlusi.

Konverentsi Moskva väljaannet saab külastada juba järgmise aasta aprillis, seni aga räägivad meie tudengid, mida huvitavat nad eelmisel üritusel õppisid. 

C++ Venemaa: kuidas see juhtus

Foto alates konverentsi album

Firmast

Selle postituse kallal töötasid kaks üliõpilast National Research University Higher School of Economics - Petersburgist:

  • Liza Vasilenko on 4. kursuse bakalaureuseõppe üliõpilane, kes õpib rakendusmatemaatika ja arvutiteaduse programmi raames programmeerimiskeeli. Olles oma esimesel ülikooliaastal C++ keelega tuttavaks saanud, omandasin edaspidi sellega töötamise kogemusi läbi praktika erialal. Minu kirg programmeerimiskeelte vastu üldiselt ja eelkõige funktsionaalse programmeerimise vastu jättis oma jälje konverentsi ettekannete valikusse.
  • Danya Smirnov on magistriprogrammi “Programmeerimine ja andmeanalüüs” 1. kursuse üliõpilane. Veel kooliajal kirjutasin olümpiaadiülesandeid C++ keeles ja siis juhtus kuidagi nii, et keel kerkis õppetegevuses pidevalt üles ja sai lõpuks põhiliseks töökeeleks. Otsustasin konverentsil osaleda, et oma teadmisi täiendada ja ka uusi võimalusi tundma õppida.

Teaduskonna juhtkond jagab uudiskirjas sageli teavet meie erialaga seotud haridusürituste kohta. Septembris nägime infot C++ Venemaa kohta ja otsustasime end kuulajateks registreerida. See on meie esimene kogemus sellistel konverentsidel osaleda.

Konverentsi struktuur

  • Aruanded

Kahe päeva jooksul lugesid eksperdid läbi 30 aruannet, mis käsitlevad palju kuumaid teemasid: keelefunktsioonide leidlik kasutamine rakendusprobleemide lahendamiseks, uue standardiga seotud keelevärskendused, kompromissid C++ disainis ja ettevaatusabinõud nende tagajärgedega töötamisel, näited huvitavat projektiarhitektuuri, aga ka mõningaid keeleinfrastruktuuri kapotialuseid üksikasju. Korraga toimus kolm etendust, kõige sagedamini kaks vene ja üks inglise keeles.

  • Arutelu tsoonid

Pärast kõnet viidi kõik küsimata küsimused ja lõpetamata arutelud spetsiaalselt ettekandjatega suhtlemiseks ette nähtud kohtadesse, mis olid varustatud markertahvlitega. Hea viis kõnedevahelise pausi veetmiseks meeldiva vestlusega.

  • Välkvestlused ja mitteametlikud arutelud

Kui soovid teha lühikest ettekannet, võid end tahvlil kirja panna õhtusele Lightning Talkile ja saad viis minutit aega, et rääkida ükskõik millest konverentsi teemal. Näiteks kiire tutvustus desinfitseerimisvahenditest C++ jaoks (mõnedele oli see uus) või lugu siinuslaine genereerimise veast, mida saab ainult kuulda, aga mitte näha.

Teine formaat on paneeldiskussioon “Südamest südamesse komitee”. Laval on mõned standardimiskomisjoni liikmed, projektoris kamin (ametlikult - siira õhkkonna loomiseks, aga naljakam tundub põhjus “sest KÕIK ON PÕLEB”), küsimusi standardi ja C++ üldise nägemuse kohta. , ilma tuliste tehniliste arutelude ja pühadeta. Selgus, et komisjonis on ka elavaid inimesi, kes ei pruugi milleski päris kindlad olla või midagi ei tea.

Holivaride fännidele jäi korpusele kolmas sündmus - BOF-i seanss “Go vs. C++”. Võtame Go-armastaja, C++-sõbra, enne seansi algust valmistavad nad koos 100500 XNUMX slaidi teemal (nt C++-i pakettide probleemid või Go-s geneeriliste ravimite puudumine) ning seejärel arutlevad nad omavahel elavalt. publikuga ja publik püüab mõista kahte vaatenurka korraga . Kui holivar algab kontekstist väljas, sekkub moderaator ja lepitab osapooled. See formaat tekitab sõltuvust: mitu tundi pärast algust said valmis vaid pooled slaidid. Lõppu tuli kõvasti kiirendada.

  • Partner seisab

Konverentsi partnerid olid saalides esindatud - stendidel räägiti jooksvatest projektidest, pakuti praktika- ja töökohti, peeti viktoriine ja väikseid konkursse ning loositi välja ka toredaid auhindu. Samas pakkusid mõned ettevõtted isegi intervjuude algfaasi läbimist, mis võib olla kasulik neile, kes tulid mitte ainult aruandeid kuulama.

Aruannete tehnilised üksikasjad

Mõlemal päeval kuulasime aruandeid. Paralleelsete ettekannete hulgast oli kohati raske ühte valida - leppisime kokku, et läheme lahku ja vahetame vaheaegadel omandatud teadmisi. Ja isegi nii tundub, et palju jääb välja. Siinkohal tahaksime rääkida mõnede meile kõige huvitavamate aruannete sisust

Erandid C++-s läbi kompilaatorite optimeerimise prisma, Roman Rusjajev

C++ Venemaa: kuidas see juhtus
Libistage ettekanded

Nagu pealkiri viitab, vaatas Roman eranditega töötamist, kasutades näitena LLVM-i. Samal ajal, neile, kes Clangi oma töös ei kasuta, võib aruanne siiski anda aimu, kuidas koodi potentsiaalselt optimeerida saaks. Seda seetõttu, et kompilaatorite ja vastavate standardteekide arendajad suhtlevad omavahel ning paljud edukad lahendused võivad kokku langeda.

Nii et erandi käsitlemiseks peate tegema palju asju: helistama käsitsemiskoodile (kui see on olemas) või vabastama praegusel tasemel ressursse ja keerama pinu kõrgemale. Kõik see toob kaasa asjaolu, et kompilaator lisab täiendavaid juhiseid kõnede jaoks, mis võivad põhjustada erandeid. Seega, kui erandit tegelikult ei tõstata, teeb programm ikkagi tarbetuid toiminguid. Üldkulude kuidagi vähendamiseks on LLVM-il mitu heuristikat olukordade määramiseks, kus erandite käsitlemise koodi pole vaja lisada või "lisa" käskude arvu vähendada.

Kõneleja uurib neist kümmekonda ja näitab nii olukordi, kus need aitavad programmi täitmist kiirendada, kui ka neid, kus need meetodid ei ole rakendatavad.

Seega viib Roman Rusjajev õpilased järeldusele, et erandeid sisaldavat koodi ei saa alati nullkuluga käivitada, ja annab järgmise nõuande:

  • raamatukogude arendamisel tasub eranditest põhimõtteliselt loobuda;
  • kui erandeid ikka vaja on, siis tasub igal võimalusel lisada igale poole noexcept (ja const) modifikaatorid, et kompilaator saaks võimalikult palju optimeerida.

Üldiselt kinnitas kõneleja seisukohta, et eranditega on kõige parem kasutada minimaalselt või neist üldse loobuda.

Aruande slaidid on saadaval järgmisel lingil: ["C++ erandid LLVM-i kompilaatori optimeerimise kaudu"]

Generaatorid, korutiinid ja muu aju lahtirulliv maiuspala, Adi Shavit

C++ Venemaa: kuidas see juhtus
Libistage ettekanded

Selle C++20 uuendustele pühendatud konverentsi üks paljudest ettekannetest jäi meelde mitte ainult oma värvika esitluse, vaid ka kogude töötlemise loogika olemasolevate probleemide selge tuvastamise poolest (silmus, tagasihelistamised).

Adi Shavit tõstab esile järgmist: praegu saadaolevad meetodid läbivad kogu kollektsiooni ega võimalda juurdepääsu mõnele sisemisele vahepealsele olekule (või teevad nad seda tagasihelistamiste puhul, kuid suure hulga ebameeldivate kõrvalmõjudega, nt Callback Hell) . Tundub, et iteraatoreid on, kuid isegi nendega pole kõik nii sujuv: puuduvad ühised sisenemis- ja väljumispunktid (algus → lõpp versus rbegin → rend ja nii edasi), pole selge, kui kaua me itereerime? Alates C++20-st on need probleemid lahendatud!

Esimene võimalus: vahemikud. Iteraatorite mähkimisega saame ühise liidese iteratsiooni alguse ja lõpu jaoks ning saame ka koostamise võimaluse. Kõik see muudab täisväärtuslike andmetöötluskonveierite ehitamise lihtsaks. Kuid kõik pole nii sujuv: osa arvutusloogikast asub konkreetse iteraatori juurutamise sees, mis võib koodi mõistmise ja silumise keerulisemaks muuta.

C++ Venemaa: kuidas see juhtus
Libistage ettekanded

Noh, sel juhul lisas C++20 korutiinid (funktsioonid, mille käitumine on sarnane Pythoni generaatoritega): täitmist saab edasi lükata, tagastades mõne praeguse väärtuse, säilitades samal ajal vahepealse oleku. Seega ei tööta me mitte ainult andmetega, nagu need paistavad, vaid ka kogu loogika kapseldamise konkreetsesse korutiini.

Kärbes on aga käes: praegu toetavad olemasolevad kompilaatorid neid vaid osaliselt ning pole ka nii kenasti juurutatud, kui me tahaksime: näiteks ei tasu veel viiteid ja ajutisi objekte korotiinides kasutada. Lisaks on mõned piirangud sellele, mis võivad olla korutiinid, ning constexpri funktsioonid, konstruktorid/hävitajad ja main ei sisaldu selles loendis.

Seega lahendavad korutiinid olulise osa probleemidest andmetöötlusloogika lihtsusega, kuid nende praegused teostused vajavad täiustamist.

Materjalid:

C++ trikid Yandex.Taxilt, Anton Polukhin

Oma erialases tegevuses pean vahel juurutama puhtalt abiasju: mähkimist mõne teegi siseliidese ja API vahel, logimist või parsimist. Sellisel juhul pole tavaliselt vaja täiendavat optimeerimist. Aga mis siis, kui neid komponente kasutatakse mõnes RuNeti kõige populaarsemas teenuses? Sellises olukorras peate ainuüksi logide tunnis töötlema terabaite! Siis loeb iga millisekund ja seetõttu tuleb appi võtta erinevaid nippe – neist rääkis Anton Poluhhin.

Võib-olla kõige huvitavam näide oli pointer-to-implementation (pimpl) mustri rakendamine. 

#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_; 
};

Selles näites soovin kõigepealt vabaneda väliste teekide päisefailidest – nii kompileeritakse kiiremini ja saate end kaitsta võimalike nimekonfliktide ja muude sarnaste vigade eest. 

Olgu, teisaldasime #include .cpp-faili: vajame mähitud API edasideklaratsiooni, samuti std::unique_ptr. Nüüd on meil dünaamilised jaotused ja muud ebameeldivad asjad, nagu andmed, mis on hajutatud paljude andmete vahel, ja vähendatud garantiid. std::aligned_storage võib selle kõigega aidata. 

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_; 
};

Ainus probleem: peate määrama iga ümbrise suuruse ja joonduse - teeme oma pimpli malli parameetritega , kasutage suvalisi väärtusi ja lisage hävitajale kontroll, et saime kõik õigesti: 

~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"
    ); 
}

Kuna T on destruktori töötlemisel juba määratletud, sõelutakse see kood õigesti ja kompileerimisetapis väljastab see vajaliku suuruse ja joondusväärtused, mis tuleb vigadena sisestada. Seega vabaneme ühe täiendava kompileerimisjooksu arvelt pakitud klasside dünaamilise jaotamise, peidame API koos juurutusega .cpp-faili ja saame ka kujunduse, mis sobib paremini protsessori vahemällu salvestamiseks.

Logimine ja sõelumine tundusid vähem muljetavaldavad ja seetõttu ei mainita seda selles ülevaates.

Aruande slaidid on saadaval järgmisel lingil: ["C++ trikid taksost"]

Kaasaegsed tehnikad koodi KUIVAS hoidmiseks, Björn Fahller

Selles kõnes näitab Björn Fahller mitut erinevat viisi, kuidas võidelda korduvate seisundikontrollide stiiliveaga:

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

Tundub tuttav? Kasutades mitmeid võimsaid C++ tehnikaid, mis on kasutusele võetud viimastes standardites, saate elegantselt rakendada samu funktsioone ilma jõudlustrahvita. Võrdlema:   

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

Fikseerimata arvu kontrollide käsitlemiseks peate kohe kasutama variadic malle ja voltimisavaldisi. Oletame, et tahame kontrollida mitme muutuja võrdsust enumi elemendiga state_type. Esimene asi, mis meelde tuleb, on kirjutada abifunktsioon is_any_of:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

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

See vahetulemus valmistab pettumuse. Siiani pole kood loetavamaks muutunud:

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

Mittetüübilised malliparameetrid aitavad olukorda veidi parandada. Nende abiga viime loendi loendatavad elemendid malli parameetrite loendisse: 

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

Kasutades mittetüüpi malli parameetris (C++17) automaatset, üldistab lähenemine lihtsalt võrdlustele mitte ainult state_type elementidega, vaid ka primitiivsete tüüpidega, mida saab kasutada mittetüüpi malli parameetritena:


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

Nende järjestikuste täiustuste abil saavutatakse kontrollide soovitud sujuv süntaks:


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);

Selles näites aitab mahaarvamisjuhend soovitada soovitud struktuurimalli parameetreid kompilaatorile, mis teab konstruktori argumentide tüüpe. 

Edasi - huvitavam. Bjorn õpetab, kuidas üldistada saadud koodi võrdlusoperaatorite jaoks väljaspool == ja seejärel suvaliste operatsioonide jaoks. Selle käigus selgitatakse kasutusnäidete abil selliseid funktsioone nagu atribuut no_unique_address (C++20) ja malliparameetrid lambda-funktsioonides (C++20). (Jah, nüüd on lambda süntaks veelgi lihtsam meelde jätta – need on neli järjestikust igat liiki sulgude paari.) Lõpplahendus, mis kasutab konstruktori detailidena funktsioone, teeb hinge väga soojaks, rääkimata lambda parimate traditsioonide väljenduskorpusest arvutus.

Lõpus ärge unustage seda lihvida:

  • Pidage meeles, et lambdad on constexpr tasuta; 
  • Lisame täiusliku edasisuunamise ja vaatame selle inetut süntaksit lambda sulguris oleva parameetripaketi suhtes;
  • Anname kompilaatorile rohkem võimalusi optimeerimiseks tingimusliku noexcept; 
  • Hoolitseme arusaadavama veaväljundi eest mallides tänu lambdade selgesõnalistele tagastamisväärtustele. See sunnib kompilaatorit enne mallifunktsiooni tegelikku väljakutsumist rohkem kontrollima – tüübikontrolli etapis. 

Täpsemalt loe loengu materjalidest: 

Meie muljed

Meie esimene osalemine C++ Venemaal oli meeldejääv oma intensiivsuse poolest. C++ Venemaast jäi mulle mulje kui siirast üritusest, kus piir treeningute ja elava suhtluse vahel on peaaegu märkamatu. Kõik, alates esinejate meeleolust ja lõpetades ürituse partnerite võistlustega, soodustab tuliseid arutelusid. Ettekannetest koosnev konverentsi sisu hõlmab üsna laia teemaderingi, sealhulgas C++ uuendusi, suurte projektide juhtumiuuringuid ja ideoloogilisi arhitektuurilisi kaalutlusi. Kuid oleks ebaõiglane ignoreerida sündmuse sotsiaalset komponenti, mis aitab ületada keelebarjääre mitte ainult C++ puhul.

Täname konverentsi korraldajaid võimaluse eest sellisel üritusel osaleda!
Võib-olla olete näinud korraldajate postitust C++ Venemaa minevikust, olevikust ja tulevikust JUG Ru blogis.

Täname lugemise eest ja loodame, et meie sündmuste ümberjutustusest oli abi!

Allikas: www.habr.com

Lisa kommentaar