C++ Oroszország: hogyan történt

Ha a darab elején azt mondod, hogy C++ kód lóg a falon, akkor a végére biztosan lábon lő.

Bjarne Stroustrup

Október 31. és november 1. között került megrendezésre Szentpéterváron a C++ Russia Piter konferencia - az egyik nagyszabású oroszországi programozási konferencia, amelyet a JUG Ru Group szervezett. A vendégelőadók között megtalálhatók a C++ Szabványügyi Bizottság tagjai, a CppCon előadói, az O'Reilly könyvek szerzői, valamint az olyan projektek karbantartói, mint az LLVM, a libc++ és a Boost. A konferencia olyan tapasztalt C++ fejlesztőknek szól, akik szeretnék elmélyíteni szakértelmüket és tapasztalatot cserélni az élő kommunikáció terén. Diákoknak, végzős hallgatóknak és egyetemi tanároknak nagyon szép kedvezményeket biztosítunk.

A konferencia moszkvai kiadása már jövő év áprilisától látogatható, de addig is diákjaink mesélnek, milyen érdekességeket tanultak a legutóbbi rendezvényen. 

C++ Oroszország: hogyan történt

Fotó innen konferencia album

Rólunk

A Szentpétervári Nemzeti Kutatói Egyetem Közgazdaságtudományi Egyetemének két hallgatója dolgozott ezen a poszton:

  • Liza Vasilenko 4. éves egyetemi hallgató, aki az Alkalmazott Matematika és Számítástechnika program részeként programozási nyelveket tanul. Miután az első egyetemi évemben megismerkedtem a C++ nyelvvel, ezt követően az iparban eltöltött gyakorlatok során szereztem tapasztalatot a nyelvvel való foglalkozás során. Általában a programozási nyelvek és különösen a funkcionális programozás iránti szenvedélyem rányomta bélyegét a konferencia beszámolóinak kiválasztására.
  • Danya Smirnov a „Programozás és adatelemzés” mesterképzés 1. éves hallgatója. Még iskolás koromban C++ nyelven írtam olimpiai feladatokat, aztán valahogy úgy alakult, hogy a nyelv folyamatosan előkerült az oktatási tevékenységekben, és végül a fő munkanyelvvé vált. Úgy döntöttem, hogy részt veszek a konferencián, hogy bővítsem tudásomat, és megismerjem az új lehetőségeket.

A hírlevélben a kar vezetése gyakran oszt meg információkat a szakterületünkhöz kapcsolódó oktatási eseményekről. Szeptemberben információkat láttunk a C++ Oroszországról, és úgy döntöttünk, hogy hallgatóként regisztrálunk. Ez az első tapasztalatunk, hogy részt veszünk ilyen konferenciákon.

Konferencia szerkezete

  • Jelentések

A két nap alatt 30 jelentést olvastak el a szakértők, amelyek számos forró témát érintenek: a nyelvi funkciók ötletes felhasználása alkalmazott problémák megoldására, az új szabvánnyal kapcsolatos közelgő nyelvi frissítések, kompromisszumok a C++ tervezésében és óvintézkedések azok következményeivel való munka során, példák érdekes projektarchitektúrát, valamint a nyelvi infrastruktúra néhány rejtett részletét. Három előadás zajlott egyszerre, leggyakrabban kettő orosz és egy angol nyelven.

  • Vita zónák

A beszéd után minden fel nem tett kérdést és befejezetlen vitát az előadókkal való kommunikációra kijelölt, jelzőtáblákkal ellátott területekre helyeztek át. Jó módja annak, hogy egy kellemes beszélgetéssel eltüntesse a beszédek közötti szünetet.

  • Villámbeszélgetések és kötetlen beszélgetések

Ha rövid beszámolót szeretnél tartani, jelentkezz a táblán az esti Villámbeszélgetésre, és kapsz öt percet, hogy bármiről beszélgess a konferencia témájáról. Például egy gyors bevezetés a C++-hoz készült fertőtlenítőkhöz (egyeseknek új volt), vagy egy sztori a szinuszhullám generálásának hibájáról, amit csak hallani lehet, de látni nem.

Egy másik formátum a „Szívvel a szívnek bizottság” panelbeszélgetés. A színpadon a szabványügyi bizottság néhány tagja, a kivetítőn egy kandalló (hivatalosan - az őszinte hangulat megteremtése végett, de viccesebbnek tűnik a „mert MINDEN ÉG” ok), kérdések a szabványról és a C++ általános víziójáról , heves technikai viták és holiwars nélkül. Kiderült, hogy a bizottságban élő emberek is vannak, akik nem biztos, hogy valamiben teljesen biztosak, vagy nem tudnak valamit.

A holivarok rajongói számára a harmadik esemény maradt a tokon - a BOF session „Go vs. C++”. Fogunk egy Go-szeretőt, egy C++-szeretőt, a foglalkozás kezdete előtt közösen elkészítenek 100500 XNUMX diát egy témáról (például csomagproblémák a C++-ban vagy a generikusok hiánya a Go-ban), majd élénk megbeszélést folytatnak egymással, a közönséggel, és a közönség egyszerre két nézőpontot próbál megérteni. Ha egy holivar kontextuson kívülre indul, a moderátor közbelép és kibékíti a feleket. Ez a formátum addiktív: több órával a kezdés után már csak a diák fele készült el. A végét nagyon fel kellett gyorsítani.

  • Partner áll

A konferencia partnerei képviseltették magukat a termekben - a standokon az aktuális projektekről beszélgettek, gyakorlati és foglalkoztatási lehetőségeket kínáltak, vetélkedőket, kisebb versenyeket tartottak, valamint szép nyereményeket sorsoltak ki. Ugyanakkor néhány cég még az interjúk kezdeti szakaszát is felajánlotta, ami hasznos lehet azoknak, akik nem csak riportokat hallgatnak.

A jelentések technikai részletei

Mindkét nap riportokat hallgattunk. Időnként nehéz volt kiválasztani egyet a párhuzamos beszámolók közül - megállapodtunk abban, hogy a szünetekben szétválunk és kicseréljük a megszerzett ismereteket. És még így is úgy tűnik, hogy sok minden kimaradt. Itt szeretnénk beszélni néhány általunk legérdekesebbnek talált jelentés tartalmáról

Kivételek a C++-ban a fordítóoptimalizálás prizmáján keresztül, Roman Rusyaev

C++ Oroszország: hogyan történt
Csúszás innen előadások

Ahogy a cím is sugallja, Roman megvizsgálta a kivételekkel való munkavégzést az LLVM példaként. Ugyanakkor azok számára, akik nem használják a Clang-t munkájuk során, a jelentés még mindig adhat némi ötletet arról, hogyan lehetne optimalizálni a kódot. Ez azért van így, mert a fordítók és a megfelelő szabványkönyvtárak fejlesztői kommunikálnak egymással, és sok sikeres megoldás egybeeshet.

Tehát egy kivétel kezeléséhez sok mindent meg kell tennie: hívja meg a kezelési kódot (ha van ilyen), vagy szabadítson fel erőforrásokat az aktuális szinten, és feljebb pörgesse a veremet. Mindez ahhoz a tényhez vezet, hogy a fordító további utasításokat ad azokhoz a hívásokhoz, amelyek potenciálisan kivételeket dobnak. Ezért, ha a kivétel ténylegesen nem merül fel, a program továbbra is szükségtelen műveleteket hajt végre. A többletterhelés valahogy csökkentése érdekében az LLVM számos heurisztikával rendelkezik az olyan helyzetek meghatározására, amikor nem kell kivételkezelő kódot hozzáadni, vagy csökkenteni lehet az „extra” utasítások számát.

Az előadó mintegy tucatnyiat megvizsgál, és bemutatja azokat a helyzeteket is, amelyekben segítik a programvégrehajtást, és azokat, ahol ezek a módszerek nem alkalmazhatók.

Így Roman Rusyaev arra a következtetésre vezeti a hallgatókat, hogy a kivételkezelést tartalmazó kódot nem mindig lehet nulla többletköltséggel végrehajtani, és a következő tanácsokat adja:

  • a könyvtárak fejlesztésénél érdemes elvileg lemondani a kivételekről;
  • ha mégis szükség van kivételekre, akkor amikor csak lehet, érdemes mindenhol noexcept (és const) módosítókat hozzáadni, hogy a fordító minél többet tudjon optimalizálni.

Általánosságban az előadó megerősítette azt a nézetet, hogy a kivételeket a legjobb, ha minimálisra használják, vagy teljesen elhagyják.

A riportdiák az alábbi linken érhetők el: ["C++ kivételek az LLVM fordítóoptimalizálás szemüvegén keresztül"]

Generátorok, korutinok és egyéb agyongöngyölítő édesség, Adi Shavit

C++ Oroszország: hogyan történt
Csúszás innen előadások

A C++20 innovációinak szentelt konferencia számos beszámolója közül az egyik nem csak a színes előadása miatt volt emlékezetes, hanem a gyűjteményfeldolgozási logikával (hurok, visszahívások) meglévő problémák egyértelmű azonosításával is.

Adi Shavit a következőket emeli ki: a jelenleg elérhető módszerek a teljes gyűjteményen átmennek, és nem biztosítanak hozzáférést valamilyen belső köztes állapothoz (illetve visszahívások esetén igen, de nagyszámú kellemetlen mellékhatással, mint például a Callback Hell) . Úgy tűnik, vannak iterátorok, de még velük sem megy minden: nincsenek közös be- és kilépési pontok (kezdet → vége vs. rbegin → rend és így tovább), nem világos, meddig fogunk iterálni? A C++20-tól kezdve ezek a problémák megoldódtak!

Első lehetőség: tartományok. Az iterátorok becsomagolásával közös felületet kapunk az iteráció elejére és végére, valamint komponálási lehetőséget is kapunk. Mindez megkönnyíti a teljes értékű adatfeldolgozási folyamatok kiépítését. De nem minden olyan zökkenőmentes: a számítási logika egy része egy adott iterátor megvalósításában található, ami bonyolíthatja a kód megértését és hibakeresését.

C++ Oroszország: hogyan történt
Csúszás innen előadások

Nos, ebben az esetben a C++20 korutinokat adott hozzá (olyan függvények, amelyek viselkedése hasonló a Python generátoraihoz): a végrehajtás elhalasztható, ha valamilyen aktuális értéket ad vissza, miközben megőriz egy köztes állapotot. Így nemcsak azt érjük el, hogy úgy dolgozunk az adatokkal, ahogy azok megjelennek, hanem azt is, hogy az összes logikát egy adott korutinba foglaljuk.

De van egy légy a szemében: jelenleg csak részben támogatják őket a meglévő fordítók, és nem is vannak olyan szépen implementálva, mint szeretnénk: például még nem érdemes hivatkozásokat és ideiglenes objektumokat használni a korutinokban. Ráadásul vannak korlátozások arra vonatkozóan, hogy melyek lehetnek korutinok, és a constexpr függvények, konstruktorok/destruktorok és main nem szerepelnek ebben a listában.

Így a korutinok a problémák jelentős részét az adatfeldolgozási logika egyszerűségével oldják meg, de jelenlegi megvalósításaik fejlesztést igényelnek.

anyagok:

C++ trükkök a Yandex.Taxitól, Anton Polukhin

Szakmai tevékenységem során néha pusztán kisegítő dolgokat kell megvalósítanom: burkolást valamilyen könyvtár belső felülete és API-ja között, naplózást vagy elemzést. Ebben az esetben általában nincs szükség további optimalizálásra. De mi van akkor, ha ezeket az összetevőket a RuNet legnépszerűbb szolgáltatásaiban használják? Ilyen helyzetben egyedül terabájtot kell feldolgoznia óránként naplófájlokból! Ekkor minden ezredmásodperc számít, és ezért különféle trükkökhöz kell folyamodnia - beszélt róluk Anton Polukhin.

A legérdekesebb példa talán a pointer-to-implementation (pimpl) minta megvalósítása volt. 

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

Ebben a példában először meg akarok szabadulni a külső könyvtárak fejlécfájljaitól – ez gyorsabban lefordítja, és megvédheti magát az esetleges névütközésektől és más hasonló hibáktól. 

Rendben, áthelyeztük az #include-ot a .cpp fájlba: szükségünk van egy előremenő nyilatkozatra a becsomagolt API-ról, valamint az std::unique_ptr-ről. Mostantól dinamikus kiosztások és egyéb kellemetlen dolgok állnak rendelkezésünkre, mint például az adatok egy csomó adat között szétszórva, és csökkentett garanciák. Az std::aligned_storage mindebben segíthet. 

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

Az egyetlen probléma: minden burkolóhoz meg kell adni a méretet és az igazítást - készítsük el a pimpl sablonunkat paraméterekkel , használjon tetszőleges értéket, és adjon hozzá egy ellenőrzést a destruktorhoz, hogy minden rendben van: 

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

Mivel a T már definiálva van a destruktor feldolgozása során, ez a kód helyesen lesz értelmezve, és a fordítási szakaszban kiadja a szükséges méretet és igazítási értékeket, amelyeket hibaként kell megadni. Így egy további fordítási futás árán megszabadulunk a becsomagolt osztályok dinamikus kiosztásától, az API-t az implementációval együtt egy .cpp fájlba rejtjük, és a processzor általi gyorsítótárazásra is alkalmasabb dizájnt kapunk.

A naplózás és az elemzés kevésbé tűnt lenyűgözőnek, ezért ebben az áttekintésben nem említjük meg.

A riportdiák az alábbi linken érhetők el: ["C++ trükkök a taxitól"]

Modern technikák a kód SZÁRON tartására, Björn Fahller

Ebben a beszédben Björn Fahller többféle módszert mutat be az ismételt állapotellenőrzés stílushibáinak leküzdésére:

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

Ismerős? A legújabb szabványokban bevezetett számos hatékony C++ technikával ugyanazt a funkcionalitást elegánsan, teljesítménybüntetés nélkül valósíthatja meg. Összehasonlítás:   

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

Rögzítetlen számú ellenőrzés kezeléséhez azonnal változó sablonokat és hajtogatási kifejezéseket kell használnia. Tegyük fel, hogy ellenőrizni akarjuk több változó egyenlőségét az enum state_type elemével. Az első dolog, ami eszünkbe jut, az, hogy írjunk egy helper függvényt:_any_of:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

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

Ez a köztes eredmény kiábrándító. A kód egyelőre nem válik olvashatóbbá:

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

A nem típusú sablonparaméterek egy kicsit javítanak a helyzeten. Segítségükkel átvisszük az enum felsorolható elemeit a sablonparaméterek listájába: 

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

Az auto használatával egy nem típusú sablonparaméterben (C++17) a megközelítés egyszerűen általánosítja az összehasonlításokat nemcsak a state_type elemekkel, hanem a primitív típusokkal is, amelyek nem típusú sablonparaméterekként használhatók:


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

Ezekkel az egymást követő fejlesztésekkel elérhető az ellenőrzések kívánt folyékony szintaxisa:


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

Ebben a példában a levonási útmutató arra szolgál, hogy javaslatot tegyen a kívánt szerkezetsablon paraméterekre a fordítónak, amely ismeri a konstruktor argumentumainak típusait. 

Tovább - érdekesebb. Bjorn megtanítja, hogyan kell általánosítani az eredményül kapott kódot az ==-on túli összehasonlító operátorokhoz, majd tetszőleges műveletekhez. Útközben olyan funkciókat ismertetünk, mint a no_unique_address attribútum (C++20) és a lambda függvények sablonparaméterei (C++20) használati példákon keresztül. (Igen, most a lambda szintaxis még könnyebben megjegyezhető – ez négy egymást követő, mindenféle zárójelpár.) A végső megoldás, amely a függvényeket konstruktorrészletként használja, nagyon megmelengeti a lelkemet, nem is beszélve a lambda legjobb hagyományaiban szereplő kifejezésről. számítás.

A végén ne felejtsd el csiszolni:

  • Ne feledje, hogy a lambdák a constexpr ingyenesek; 
  • Tegyük hozzá a tökéletes továbbítást, és nézzük meg a csúnya szintaxisát a lambda-zárás paramétercsomagjával kapcsolatban;
  • Adjunk a fordítónak több lehetőséget az optimalizálásra feltételes noexcept; 
  • Gondoskodjunk az érthetőbb hibakimenetről a sablonokban a lambdas explicit visszatérési értékeinek köszönhetően. Ez arra kényszeríti a fordítót, hogy több ellenőrzést végezzen, mielőtt a sablon függvény ténylegesen meghívásra kerülne – a típusellenőrzési szakaszban. 

Részletekért tekintse meg az előadás anyagait: 

A benyomásaink

Első részvételünk a C++ Oroszországban az intenzitásáról volt emlékezetes. A C++ Russia egy őszinte esemény benyomását keltette bennem, ahol szinte észrevehetetlen a határvonal az edzés és az élő kommunikáció között. Minden, az előadók hangulatától a rendezvénypartnerek versenyeiig minden kedvez a heves vitáknak. A jelentésekből álló konferencia tartalma meglehetősen széles témakört ölel fel, beleértve a C++ innovációkat, a nagy projektek esettanulmányait és az ideológiai építészeti megfontolásokat. Igazságtalan lenne azonban figyelmen kívül hagyni az esemény társadalmi komponensét, amely nemcsak a C++ nyelvvel kapcsolatos nyelvi akadályok leküzdését segíti elő.

Köszönjük a konferencia szervezőinek a lehetőséget, hogy részt vehettünk egy ilyen rendezvényen!
Láthattad a szervezők bejegyzését a C++ Oroszország múltjáról, jelenéről és jövőjéről a JUG Ru blogon.

Köszönjük, hogy elolvasta, és reméljük, hogy az események újrabeszélése hasznos volt!

Forrás: will.com

Hozzászólás