C++ Rusija: kako se je zgodilo

Če na začetku predstave rečeš, da na steni visi koda C++, te bo na koncu gotovo strlo v nogo.

Bjarne Stroustrup

Od 31. oktobra do 1. novembra je v Sankt Peterburgu potekala konferenca C++ Russia Piter - ena obsežnejših programerskih konferenc v Rusiji, ki jo organizira JUG Ru Group. Med povabljenimi govorniki so člani odbora za standarde C++, govorci CppCon, avtorji knjig O'Reilly in vzdrževalci projektov, kot so LLVM, libc++ in Boost. Konferenca je namenjena izkušenim C++ razvijalcem, ki želijo poglobiti svoje strokovno znanje in izmenjati izkušnje v živi komunikaciji. Za študente, podiplomske študente in visokošolske učitelje so zagotovljeni zelo ugodni popusti.

Moskovsko edicijo konference bo mogoče obiskati že aprila prihodnje leto, medtem pa vam bodo naši študentje povedali, kaj zanimivega so izvedeli na zadnjem dogodku. 

C++ Rusija: kako se je zgodilo

Fotografija iz konferenčni album

O nas

Na tem delovnem mestu sta delala dva študenta Nacionalne raziskovalne univerze Visoka šola za ekonomijo - St. Petersburg:

  • Liza Vasilenko je študentka 4. letnika dodiplomskega študija programskih jezikov v okviru programa Uporabna matematika in računalništvo. Ker sem se z jezikom C++ seznanil v prvem letniku na univerzi, sem nato pridobil izkušnje z njim prek prakse v industriji. Moja strast do programskih jezikov nasploh in še posebej funkcionalnega programiranja je pustila pečat pri izboru poročil na konferenci.
  • Danya Smirnov je študentka 1. letnika magistrskega programa "Programiranje in analiza podatkov". Še v šoli sem pisal naloge za olimpijado v C++, nato pa se je nekako zgodilo, da se je jezik nenehno pojavljal v izobraževalnih dejavnostih in sčasoma postal glavni delovni jezik. Za sodelovanje na konferenci sem se odločil, da izboljšam svoje znanje in spoznam nove priložnosti.

V glasilu vodstvo fakultete pogosto deli informacije o izobraževalnih dogodkih, povezanih z našo specialnostjo. Septembra smo videli informacije o C++ Rusija in se odločili, da se registriramo kot poslušalci. To je naša prva izkušnja sodelovanja na tovrstnih konferencah.

Struktura konference

  • Poročila

V dveh dneh so strokovnjaki prebrali 30 poročil, ki so pokrivala številne vroče teme: domiselne uporabe jezikovnih funkcij za reševanje uporabnih problemov, prihajajoče jezikovne posodobitve v povezavi z novim standardom, kompromise pri načrtovanju C++ in previdnostne ukrepe pri delu z njihovimi posledicami, primere zanimive projektne arhitekture, kot tudi nekaj skritih podrobnosti jezikovne infrastrukture. Istočasno so potekale tri predstave, največkrat dve v ruščini in ena v angleščini.

  • Območja za razprave

Po govoru so bila vsa nezastavljena vprašanja in nedokončane razprave prenesene v posebej namenjene prostore za komunikacijo z govorci, opremljene z označevalnimi tablami. Dober način za krajšanje odmora med govori s prijetnim pogovorom.

  • Svetlobni pogovori in neformalne razprave

Če želite podati kratko poročilo, se lahko na tabli prijavite za večerni Lightning Talk in si zagotovite pet minut časa za pogovor o čemer koli o temi konference. Na primer hiter uvod v sanitizerje za C++ (za nekatere je bil nov) ali zgodba o napaki v sinusnem ustvarjanju valov, ki jo je mogoče le slišati, ne pa tudi videti.

Druga oblika je panelna razprava »Od srca do srca«. Na odru nekaj članov odbora za standardizacijo, na projektorju kamin (uradno - za ustvarjanje iskrenega vzdušja, bolj smešen pa se zdi razlog "ker VSE GORI"), vprašanja o standardu in splošni viziji C++ , brez burnih tehničnih razprav in holiwarjev. Izkazalo se je, da so v komisiji tudi živi ljudje, ki morda v nekaj niso popolnoma prepričani ali česa ne vedo.

Za ljubitelje holivarjev je tretji dogodek ostal na primeru - seja BOF "Go vs. C++". Vzamemo ljubitelja Goa, ljubitelja C++, pred začetkom seje skupaj pripravita 100500 prosojnic na temo (kot so težave s paketi v C++ ali pomanjkanje generičnih v Go), nato pa med seboj živahno razpravljajo in z občinstvom, in občinstvo poskuša razumeti dve stališči hkrati. Če se holivar začne izven konteksta, posreduje moderator in spravi strani. Ta oblika je zasvojenost: nekaj ur po začetku je bila dokončana samo polovica diapozitivov. Konec je bilo treba močno pospešiti.

  • Partner stoji

Partnerji konference so bili zastopani v dvoranah – na stojnicah so govorili o aktualnih projektih, ponujali pripravništva in zaposlitve, izvajali kvize in manjša tekmovanja ter žrebali lepe nagrade. Hkrati so nekatera podjetja celo ponudila, da gredo skozi začetne faze intervjujev, kar bi lahko bilo koristno za tiste, ki niso prišli le poslušati poročil.

Tehnične podrobnosti poročil

Oba dneva smo poslušali poročila. Včasih je bilo med vzporednimi poročili težko izbrati eno – dogovorili smo se, da se razdelimo in med odmori izmenjamo pridobljeno znanje. In kljub temu se zdi, da je marsikaj izpuščenega. Tukaj bi radi spregovorili o vsebini nekaterih poročil, ki so se nam zdela najbolj zanimiva

Izjeme v C++ skozi prizmo optimizacij prevajalnika, Roman Rusyaev

C++ Rusija: kako se je zgodilo
Diapozitiv iz predstavitve

Kot nakazuje naslov, je Roman obravnaval delo z izjemami na primeru LLVM. Hkrati lahko za tiste, ki pri svojem delu ne uporabljajo Clanga, poročilo še vedno daje nekaj ideje o tem, kako bi lahko kodo potencialno optimizirali. To je tako, ker razvijalci prevajalnikov in ustreznih standardnih knjižnic komunicirajo med seboj in lahko sovpadajo številne uspešne rešitve.

Za obravnavo izjeme morate torej narediti veliko stvari: poklicati kodo za obravnavo (če obstaja) ali sprostiti vire na trenutni ravni in zavrteti sklad višje. Vse to vodi do dejstva, da prevajalnik doda dodatna navodila za klice, ki potencialno vržejo izjeme. Zato bo program še vedno izvajal nepotrebna dejanja, če izjema dejansko ni sprožena. Da bi nekako zmanjšal stroške, ima LLVM več hevristik za določanje situacij, v katerih ni treba dodati kode za obravnavanje izjem ali pa je mogoče zmanjšati število "dodatnih" navodil.

Govornik jih preuči približno ducat in prikaže tako situacije, ko pomagajo pospešiti izvajanje programa, kot tiste, kjer te metode niso uporabne.

Tako Roman Rusyaev vodi študente do zaključka, da kode, ki vsebuje obravnavanje izjem, ni mogoče vedno izvesti brez dodatnih stroškov, in daje naslednji nasvet:

  • pri razvoju knjižnic velja načeloma opustiti izjeme;
  • če so še vedno potrebne izjeme, je vedno, ko je to mogoče, vredno povsod dodati modifikatorje noexcept (in const), da lahko prevajalnik čim bolj optimizira.

Govornik je na splošno potrdil stališče, da je izjeme najbolje uporabljati čim manj ali pa jih v celoti opustiti.

Diapozitivi poročila so na voljo na naslednji povezavi: [»Izjeme C++ skozi optimiranje prevajalnika LLVM«]

Generatorji, korutine in druge sladkosti za odvijanje možganov, Adi Shavit

C++ Rusija: kako se je zgodilo
Diapozitiv iz predstavitve

Eno od številnih poročil na tej konferenci, posvečenih inovacijam v C++20, je bilo nepozabno ne le zaradi barvite predstavitve, ampak tudi zaradi jasne opredelitve obstoječih težav z logiko obdelave zbirk (zanka for, povratni klici).

Adi Shavit izpostavlja naslednje: trenutno razpoložljive metode gredo skozi celotno zbirko in ne omogočajo dostopa do nekega notranjega vmesnega stanja (oziroma ga v primeru povratnih klicev, vendar z velikim številom neprijetnih stranskih učinkov, kot je Callback Hell) . Zdi se, da obstajajo iteratorji, vendar tudi z njimi ni vse tako gladko: ni skupnih vstopnih in izstopnih točk (začetek → konec proti rbegin → rend in tako naprej), ni jasno, kako dolgo bomo ponavljali? Začenši s C++20 so te težave rešene!

Prva možnost: obsegi. Z ovijanjem iteratorjev dobimo skupen vmesnik za začetek in konec iteracije, dobimo pa tudi možnost sestavljanja. Vse to olajša gradnjo polnopravnih cevovodov za obdelavo podatkov. Vendar ni vse tako gladko: del računske logike se nahaja znotraj izvedbe določenega iteratorja, kar lahko zaplete kodo pri razumevanju in odpravljanju napak.

C++ Rusija: kako se je zgodilo
Diapozitiv iz predstavitve

No, za ta primer je C++20 dodal korutine (funkcije, katerih vedenje je podobno generatorjem v Pythonu): izvajanje se lahko odloži tako, da se vrne neka trenutna vrednost, medtem ko se ohrani vmesno stanje. Tako dosežemo ne samo delo s podatki, kot so prikazani, ampak tudi enkapsulacijo celotne logike v določeno korutino.

Toda tu je muha v mazilu: trenutno jih le delno podpirajo obstoječi prevajalniki in tudi niso implementirani tako lepo, kot bi si želeli: na primer, še ni vredno uporabljati referenc in začasnih objektov v korutinah. Poleg tega obstaja nekaj omejitev glede tega, kaj so lahko korutine, in funkcije constexpr, konstruktorji/destruktorji in main niso vključeni na ta seznam.

Korutine tako rešujejo pomemben del težav s preprostostjo logike obdelave podatkov, vendar pa je treba njihove trenutne implementacije izboljšati.

Materiali:

C++ triki iz Yandex.Taxi, Anton Polukhin

Pri svojih poklicnih dejavnostih moram včasih implementirati povsem pomožne stvari: ovoj med notranjim vmesnikom in API-jem neke knjižnice, beleženje ali razčlenjevanje. V tem primeru običajno ni potrebe po dodatni optimizaciji. Kaj pa, če se te komponente uporabljajo v nekaterih najbolj priljubljenih storitvah v RuNetu? V takšni situaciji boste morali obdelati terabajte dnevnikov na uro sami! Takrat šteje vsaka milisekunda in zato se morate zateči k raznim trikom - o njih je govoril Anton Polukhin.

Morda najbolj zanimiv primer je bila implementacija vzorca od kazalca do implementacije (pimpl). 

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

V tem primeru se najprej želim znebiti datotek glave zunanjih knjižnic - to bo prevedlo hitreje, vi pa se lahko zaščitite pred morebitnimi konflikti imen in drugimi podobnimi napakami. 

V redu, premaknili smo #include v datoteko .cpp: potrebujemo vnaprejšnjo deklaracijo zavitega API-ja, pa tudi std::unique_ptr. Zdaj imamo dinamične dodelitve in druge neprijetne stvari, kot so podatki, razpršeni po množici podatkov, in zmanjšana jamstva. std::aligned_storage lahko pomaga pri vsem tem. 

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

Edina težava: določiti morate velikost in poravnavo za vsak ovoj - naredimo našo predlogo pimpl s parametri , uporabimo nekaj poljubnih vrednosti in destruktorju dodamo preverjanje, ali imamo vse prav: 

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

Ker je T že definiran pri obdelavi destruktorja, bo ta koda pravilno razčlenjena in na stopnji prevajanja bo izpisala zahtevane velikosti in vrednosti poravnave, ki jih je treba vnesti kot napake. Tako se za ceno enega dodatnega prevajanja znebimo dinamičnega dodeljevanja zavitih razredov, API skrijemo v datoteko .cpp z implementacijo in dobimo tudi zasnovo, ki je primernejša za predpomnjenje s strani procesorja.

Beleženje in razčlenjevanje sta se zdela manj impresivna in zato v tem pregledu ne bosta omenjena.

Diapozitivi poročila so na voljo na naslednji povezavi: ["C++ triki iz Taxi"]

Sodobne tehnike za ohranjanje kode na SUHEM, Björn Fahller

V tem govoru Björn Fahller pokaže več različnih načinov za boj proti slogovni napaki ponavljajočih se preverjanj pogojev:

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

Zveni znano? Z uporabo več zmogljivih tehnik C++, uvedenih v nedavnih standardih, lahko elegantno implementirate isto funkcionalnost brez kakršnega koli poslabšanja zmogljivosti. Primerjaj:   

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

Za obdelavo nedoločenega števila preverjanj morate takoj uporabiti spremenljive predloge in zgibne izraze. Predpostavimo, da želimo preveriti enakost več spremenljivk elementu state_type enuma. Prva stvar, ki mi pride na misel, je pisanje pomožne funkcije 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) || ...); 
}

Ta vmesni rezultat je razočaranje. Zaenkrat koda ni postala bolj berljiva:

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

Parametri predloge, ki niso tipi, bodo pomagali nekoliko izboljšati situacijo. Z njihovo pomočjo bomo števne elemente enuma prenesli na seznam parametrov predloge: 

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

Z uporabo auto v parametru predloge brez tipa (C++17) se pristop preprosto posploši na primerjave ne samo z elementi state_type, ampak tudi s primitivnimi tipi, ki jih je mogoče uporabiti kot parametre predloge brez tipa:


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

S temi zaporednimi izboljšavami je dosežena želena tekoča sintaksa za preverjanja:


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

V tem primeru vodnik za odbitke služi za predlaganje želenih parametrov strukturne predloge prevajalniku, ki pozna tipe argumentov konstruktorja. 

Nadalje - bolj zanimivo. Bjorn uči, kako posplošiti dobljeno kodo za primerjalne operatorje, ki presegajo ==, in nato za poljubne operacije. Na poti so funkcije, kot so atribut no_unique_address (C++20) in parametri predloge v lambda funkcijah (C++20), razložene s primeri uporabe. (Da, zdaj si je sintakso lambda še lažje zapomniti - to so štirje zaporedni pari oklepajev vseh vrst.) Končna rešitev, ki uporablja funkcije kot podrobnosti konstruktorja, mi resnično ogreje dušo, da ne omenjam izraza tuple v najboljših tradicijah lambda račun.

Na koncu ga ne pozabite polirati:

  • Ne pozabite, da so lambde constexpr brezplačne; 
  • Dodajmo popolno posredovanje in poglejmo njegovo grdo sintakso v povezavi s paketom parametrov v zaprtju lambda;
  • Dajmo prevajalniku več možnosti za optimizacije s pogojnim noexceptom; 
  • Poskrbimo za bolj razumljiv izpis napak v predlogah zahvaljujoč eksplicitnim povratnim vrednostim lambda. To bo prisililo prevajalnik, da izvede več preverjanj, preden se funkcija predloge dejansko pokliče - v fazi preverjanja tipa. 

Za podrobnosti si oglejte gradivo predavanj: 

Naši vtisi

Naša prva udeležba na C++ Russia je bila nepozabna zaradi svoje intenzivnosti. C++ Russia sem dobil vtis kot iskren dogodek, kjer je meja med treningom in komunikacijo v živo skoraj neopazna. Vse, od razpoloženja govornikov do tekmovanja partnerjev dogodka, je ugodno za burne razprave. Vsebina konference, sestavljena iz poročil, pokriva precej širok spekter tem, vključno z inovacijami C++, študijami primerov velikih projektov in ideološkimi arhitekturnimi premisleki. Vendar bi bilo nepošteno zanemariti socialno komponento dogodka, ki pomaga premagovati jezikovne ovire ne le v zvezi s C++.

Organizatorjem konference se zahvaljujemo za priložnost sodelovanja na takšnem dogodku!
Morda ste videli objavo organizatorjev o preteklosti, sedanjosti in prihodnosti C++ Rusija na blogu JUG Ru.

Hvala za branje in upamo, da je bilo naše ponavljanje dogodkov v pomoč!

Vir: www.habr.com

Dodaj komentar