Ha a darab elején azt mondod, hogy a falon egy C++ kód lóg, akkor a végén annak feltétlenül meg kell sütnie a lábad.
Bjarne Stroustrup
Október 31-től november 1-ig Szentpéterváron zajlott a C++ Russia Piter konferencia, amely Oroszország egyik legnagyobb programozási konferenciája, a JUG Ru Group szervezésében. A meghívott előadók között szerepeltek a C++ szabványosítási bizottság tagjai, a CppCon előadói, az O’Reilly kiadó írói, valamint olyan projektek karbantartói, mint az LLVM, libc++ és Boost. A konferencia a tapasztalt C++ fejlesztők számára készült, akik szeretnék elmélyíteni tudásukat és tapasztalatokat cserélni személyes interakciók során. A diákok, posztgraduális hallgatók és egyetemi oktatók jelentős kedvezményekre számíthatnak.
A konferencia moszkvai kiadását már a következő év áprilisában látogathatjuk, de addig a hallgatóink mesélnek arról, mit tanultak az esemény során.

Kép a
Rólunk
Ezen a poszton a HSE Szentpétervári Egyetem két hallgatója dolgozott:
- Liza Vasilenko – a 4. éves alapképzéses hallgató, aki a 'Programozási nyelvek' irányt tanulmányozza az 'Alkalmazott matematika és informatika' program keretében. Az első egyetemi évében ismerkedett meg a C++ nyelvvel, majd a gyakorlatok során szerzett tapasztalatokat a szakmában. Az általános programozási nyelvek és különösen a funkcionális programozás iránti érdeklődésének hatása volt a konferencián választott előadásokra.
- Danya Smirnov – az 'Adatprogramozás és -elemzés' mesterképzés első éves hallgatója. Még a középiskolában C++-ban írt versenyfeladatokat, majd valahogy a nyelv folyamatosan felbukkant az oktatási tevékenységeiben, és végül fő munkává vált. A konferencián való részvétel célja az volt, hogy fejlessze tudását és új lehetőségekről értesüljön.
Az oktatási eseményekkel kapcsolatos információkat a kar vezetősége gyakran megosztja a hírlevélben, amelyek a mi szakterületünkhöz kapcsolódnak. Szeptemberben láttuk a C++ Russia-ról szóló információkat, és úgy döntöttünk, hogy beiratkozunk hallgatóként. Ez a mi első tapasztalatunk hasonló konferenciákon.
A konferencia felépítése
Előadások
Két nap alatt a szakértők 30 előadást hallgattak meg, amelyek sok izgalmas témát érintettek: a nyelv jellemzőinek ügyes alkalmazásai gyakorlati feladatok megoldására, a nyelv közelgő frissítései az új szabványhoz kapcsolódóan, kompromisszumok a C++ designjában és óvintézkedések a következmények kezelésére, érdekes projektek architektúrájának példái, valamint a nyelv infrastruktúrájának néhány háttérinformációja. Egy időben 3 előadás zajlott, általában kettő oroszul és egy angolul.
Beszélgetési zónák
Az előadások után a megválaszolatlan kérdéseket és befejezetlen diskurzusokat a kérdezőkkel való kommunikációs zónákba helyezték át, ahol táblák álltak rendelkezésre. Jó lehetőség volt eltölteni a szünetet kellemes beszélgetéssel.
Villámelőadások és informális viták
Ha valaki szeretne egy rövid előadást tartani, az a táblán feliratkozhat egy esti Villámelőadásra és öt percet kaphat, hogy bármiről beszéljen a konferencia témájában. Például egy gyors bevezetés a C++ sanitizerekhez (ami néhányaknak újdonság volt) vagy egy történet a szinuszhullám generálásában található hibáról, amit csak hallani lehet, de nem látni.
Egy másik formátum a 'Beszélgessünk a bizottsággal' panelbeszélgetés. A színpadon a szabványügyi bizottság néhány tagja, a vetítőn egy kandalló (hivatalosan a meghitt légkör megteremtésére, de az 'ÖSSZES TŰZBEN VAN' indoklás mulatságosabbnak tűnik), a kérdések a szabványról és a C++ általános elképzeléséről szóltak, heves technikai viták és viták nélkül. Kiderült, hogy a bizottságban is élő emberek ülnek, akik nem mindig biztosak valamiben, vagy amiről nem tudnak.
A viták szerelmeseinek maradt a harmadik esemény — BOF ülés 'Go a C++ ellen'. Egy Go-rajongót és egy C++-rajongót veszünk, az ülés előtt együtt készítenek 100500 diát a témában (mint például problémák a C++ csomagjaival vagy a generikusok hiánya a Go-ban), majd élénk vitát folytatnak egymással és a nézőkkel, akik megpróbálnak két szempontot egyszerre megérteni. Ha a vita megoldatlanul kezdődik - a moderátor közbelép és kiegyenlíti az álláspontokat. Ez a formátum magával ragadó: néhány órával a kezdés után még csak a diák felénél tartottunk. A végén nagyon fel kellett gyorsítani a dolgokat.
Partnerek standjai
A konferencia partnerei a hallokban mutatkoztak be – a standokon aktuális projektekről beszéltek, gyakornoki lehetőségeket és munkalehetőségeket kínáltak, kvízeket és kisebb versenyeket tartottak, valamint kellemes ajándékokat sorsoltak ki. Emellett néhány cég még az interjú első lépéseit is felajánlotta, ami hasznos lehet azok számára, akik nemcsak előadásokat hallgatni jöttek.
A beszámolók technikai részletei
Két napon át hallgattuk a beszámolókat. Néha nehéz volt egy előadást választani a párhuzamosan zajló programok közül – megállapodtunk, hogy különválunk, és a szünetekben megosztjuk egymással a megszerzett tudást. Még így is úgy tűnik, hogy sok minden kimaradt. Itt szeretnénk bemutatni néhány olyan előadás tartalmát, amelyek számunkra a legérdekesebbek voltak.
Kivételkezelés C++-ban a fordítóoptimalizációk tükrében, Roman Ruseajev

Dia a
Ahogy a cím is sugallja, Roman az LLVM példáján keresztül vizsgálta a kivételekkel való munkát. Akik nem használják a Clangot, azok számára is nyújthat némi betekintést abba, hogy a kód hogyan lehet potenciálisan optimalizálva. Ez azért van, mert a fordítóprogramok és a megfelelő standard könyvtárak fejlesztői kommunikálnak egymással, és sok sikeres megoldás hasonló lehet.
Tehát a kivételkezeléshez számos lépést kell végrehajtani: hívni kell a kezelő kódot (ha van) vagy felszabadítani az erőforrásokat a jelenlegi szinten, valamint feltekerni a verem egy magasabb szintjét. Mindez oda vezet, hogy a potenciálisan kivételt okozó hívásokhoz a fordító többletutasításokat ad hozzá. Ezért ha a kivételt valójában nem váltják ki, a program akkor is szükségtelen lépéseket fog végrehajtani. Az overhead csökkentése érdekében az LLVM-ben van néhány heurisztika, amely meghatározza azokat a helyzeteket, ahol a kivételkezelő kódot nem szükséges hozzáadni, vagy csökkenteni lehet a "felesleges" utasítások számát.
A beszámoló szerzője körülbelül tucatnyi ilyen esetet vizsgál, és megmutatja, hogy azok a helyzetek, ahol ezek segítenek a program végrehajtásának felgyorsításában, és azokat is, ahol ezek a módszerek nem alkalmazhatók.
Így Roman Ruseajev azt a következtetést vonja le a hallgatóság számára, hogy a kivételkezeléssel rendelkező kód nem mindig hajtható végre nulla overhead mellett, és a következő tanácsokat adja:
- A könyvtárak fejlesztésekor érdemes elvetni a kivételeket;
- ha mégis szükség van kivételekre, lehetőség szerint mindenütt érdemes noexcept (és const) módosítókat hozzáadni, hogy a fordító minél több optimalizálást végezzen.
Összességében az előadó megerősítette azt a véleményt, hogy a kivételeket a lehető legkevésbé érdemes használni, vagy akár teljesen el kell kerülni őket.
A prezentáció diáit az alábbi linken érheti el:
Generátorok, korutinok és egyéb agytornásztató édességek, Adi Shavit

Dia a
Ez a konferencia egyik sok bemutatója, amely a C++20 újdonságainak szentelődött, nemcsak a színesen megformált prezentációval emlékezetes, hanem a gyűjtemények logikai feldolgozásának meglévő problémáinak egyértelmű meghatározásával is (for ciklus, callback-ek).
Adi Shavit a következő problémákat emeli ki: az eddigi módszerek egész gyűjteményen keresztül haladnak, és e közben nem adnak hozzáférést valamilyen belső köztes állapothoz (vagy csak callback esetén, de ez sok kellemetlen mellékhatással jár, például a Callback Hell-lel). Úgy tűnik, vannak iterátorok, de velük sem minden zökkenőmentes: nincs közös belépési és kilépési pont (begin → end szemben rbegin → rend és így tovább), nem világos, mennyire fogunk iterálni. A C++20-tól kezdve ezek a problémák megoldódnak!
Az első lehetőség: ranges. Az iterátorok fölötti burkolás révén közös interfészt kapunk az iteráció megkezdésére és befejezésére, valamint a kompozíció lehetőségét. Mindez lehetővé teszi a teljes adatfeldolgozó csatornák könnyű felépítését. De nem minden zökkenőmentes: a számítás logikájának egy része a konkrét iterátor megvalósításán belül van, ami megnehezíti a kód olvashatóságát és hibakeresését.

Dia a
Nos, erre az esetre a C++20 a korutinokat is bevezeti (olyan funkciók, amelyek viselkedése hasonló a Pythonban található generátorokhoz): a végrehajtás késleltethető, egy aktuális érték visszatérítésekor megőrizve a köztes állapotot. Így nemcsak az adatok megjelenésük szerinti feldolgozását érjük el, hanem minden logikát beágyazunk a konkrét korutinába.
De van egy kis árnyoldal: jelenleg csak részben támogatják a meglévő fordítók, és nem úgy valósították meg, ahogyan szeretnénk: például még nem ajánlott coroutine-ökben mutatókat és ideiglenes objektumokat használni. Ezenkívül van néhány korlátozás is, hogy mi lehet coroutine, és a constexpr függvények, konstruktorok/destruktorok, valamint a main nem tartozik ebbe a listába.
Így a coroutine-ok megoldják a adatfeldolgozási logika egyszerűsítésének jelentős részét, de jelenlegi megvalósításaik további finomítást igényelnek.
Anyagok:
- Diák a C++ Oroszországtól —
C++ trükkök a Yandex.Taxi-ból, Anton Poluhin
Szakmai tevékenységem során néha kifejezetten segédleteket kell megvalósítanom: egy belső interfész és valamilyen könyvtár API közötti burkoló, naplózás vagy elemzés. Ilyenkor általában nincs szükség további optimalizálásra. De mi van, ha ezek az összetevők a legnépszerűbb orosz nyelvű szolgáltatások egyike által használatosak? Egy ilyen helyzetben óránként terabájtokat kell feldolgozni csak a naplókból! Akkor minden milliszekundum számít, és ezért különféle trükkökhöz kell folyamodni — ezekről beszélt Anton Poluhin.
Valószínűleg a legérdekesebb példa 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 is szeretnénk megszabadulni a külső könyvtárak fejlécfájljaitól — így gyorsabban fog fordulni, és megvédhetjük magunkat a névütközések és más hasonló hibák lehetőségétől.
Jól van, áthelyeztük a #include-ot a .cpp fájlba: szükség van a burkolt API forward-declaration-jére, valamint std::unique_ptr-ra. Most már dinamikus allokációink és más problémák vannak, mint a tömbön szétszórt adatok és csökkentett garanciák. Mindezhez segíthet a std::aligned_storage.
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 data_;
};Az egyetlen probléma: minden burkolónál meg kell határoznunk a méretet és az igazítást — a mi pimpl-ünket sablonosítjuk paraméterekkel, használunk valamilyen önkényes értéket, és a destruktorba beillesztünk egy ellenőrzést, hogy mindent jól találtunk-e.
~FastPimpl() noexcept {
validate();
Ptr()->~T();
}
template
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 destruktorának feldolgozása már létre van hozva, a kód helyesen fog működni, és a fordítási fázisban megfelelő méret- és igazítási értékeket fog kiadni, amelyeket meg kell adni. Így egy további fordítás futásának árán megszabadulunk a dinamikus allokációtól a burkolt osztályok esetében, elrejtve az API-t egy .cpp fájlba a megvalósítással, ráadásul egy processzor által cache-elhetőbb struktúrát is kapunk.
A naplózás és a parszolás kevésbé tűnt lenyűgözőnek, ezért ezen a véleményen nem fogunk beszélni.
A prezentáció diáit az alábbi linken érheti el:
Modern technikák a DRY szabálya megtartására, Björn Fahller
Ebben az előadásban Björn Fahller több különböző módszert mutat be a stílusbeli hiányosságok, például a feltételek ismétlődő ellenőrzésének leküzdésére:
assert(a == IDLE || a == CONNECTED || a == DISCONNECTED);Ismerős? Néhány erőteljes C++ technikával, amelyek nemrégiben kerültek bevezetésre, elegánsan valósíthatjuk meg ugyanazt a funkcionalitást teljesítményveszteség nélkül. Hasonlítsuk össze:
assert(a == any_of(IDLE, CONNECTED, DISCONNECTED));Az eltérő számú ellenőrzés kezelésére variadic template-eket és fold kifejezéseket érdemes használni. Tegyük fel, hogy több változó egy enum ’state_type’ elemével való egyenlőségét akarjuk ellenőrizni. Az első gondolat az, hogy írjunk egy segédfüggvényt is_any_of:
enum state_type { IDLE, CONNECTED, DISCONNECTED };
template
bool is_any_of(state_type s, const Ts& ... ts) {
return ((s == ts) || ...);
}
Ez a közbenső eredmény csalódást kelt. A kód továbbra sem válik olvashatóbbá:
assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED)); A nem típusú template paraméterek kissé segíthetnek a helyzet javításában. Ezek segítségével az enum elemeit a template paraméterek listájába helyezzük:
template
bool is_any_of(state_type t) {
return ((t == states) | ...);
}
assert(is_any_of(state)); Itt auto használatával a nem típusú template paraméterben (C++17) a megközelítés egyszerűen általánosítható nemcsak a state_type elemeivel, hanem primitív típusokkal is, amelyek non-type template paraméterekként használhatók:
sablon
bool is_any_of(const T& t) {
return ((t == alternatívák) | ...);
}Ezekkel a fokozatos fejlesztésekkel elérjük a kívánt zökkenőmentes szintaxist a vizsgálatokhoz:
sablon
struktur any_of : privát std::tuple {
// örökölni fogjuk a tuple konstruktőreit
using std::tuple::tuple;
sablon
bool operator ==(const T& t) const {
return std::apply(
[&t](const auto& ... ts) {
return ((ts == t) || ...);
},
static_cast<const std::tuple&>(*this));
}
};
sablon
any_of(Ts ...) -> any_of;
assert(any_of(IDLE, DISCONNECTING, DISCONNECTED) == state);
Ebben a példában a deduction guide a kívánt sablonparaméterek megadására szolgál a struktúra számára a fordítónak, amely ismeri a konstruktor argumentumainak típusait.
Tovább – érdekesebb. Björn megtanítja, hogyan lehet általánosítani a kapott kódot a != operátorokra is, majd pedig tetszőleges műveletekre. Közben példákon keresztül bemutatásra kerülnek olyan jellemzők, mint a no_unique_address attribute (C++20) és a sablonparaméterek lambda-függvényekben (C++20). (Igen, most már a lambda szintaxis is könnyebben megjegyezhető – négy egymást követő zárójelpár mindenféle változatban.) A végső megoldás, amely függvények mint konstruktoralkatrészek használatával készült, nagyon megörvendeztet, nem beszélve a tuple kifejezésről, a lambda számítás legjobb hagyományai szerint.
A végén ne felejtsük el felfrissíteni a kódot:
- Emlékezzünk, hogy a lambdák – constexpr ingyen;
- Adjunk hozzá perfect forwardingot, és nézzük meg annak csúf szintaxisát a parameter pack keretében a lambdák lezárásakor;
- Adjunk a fordítónak több lehetőséget az optimalizálásra conditional noexcept használatával;
- Figyeljünk a hibaüzenetek érthetőbb megjelenítésére a sablonokban kifejezett visszatérési értékek segítségével. Ez arra kényszeríti a fordítót, hogy több ellenőrzést végezzen a sablonfüggvény tényleges meghívása előtt – a típusellenőrzés szakaszában.
Részletekért forduljon a tananyagokhoz:
- Az előadás diája:
Benyomásaink
Első részvételünk a C++ Oroszországon emlékezetes volt gazdagságával. Az C++ Oroszország olyan esemény benyomását keltette, ahol a tanulás és a személyes kapcsolatok közötti határ szinte érzékelhetetlen. Minden, a előadók hangulatától kezdve a rendezvény partnereinek versenyeiig, élénk eszmecserékre ösztönöz. A konferencia tartalmi része, amely az előadásokból áll, meglehetősen széles spektrumot ölel fel, beleértve a C++ újításait, nagy projektek gyakorlati példáit és ideológiai architekturális megfontolásokat. De igazságtalan lenne figyelmen kívül hagyni az esemény társadalmi aspektusát is, amely hozzájárul a nyelvi akadályok leküzdéséhez nemcsak a C++ kapcsán.
Köszönjük a konferencia szervezőinek, hogy részt vehetünk egy ilyen eseményen!
A szervezők posztját a C++ Oroszországról, múltjáról, jelenéről és jövőjéről láthatták .
Köszönjük a figyelmet, és reméljük, hogy eseményeink összefoglalója hasznos volt!
Forrás: habr.com
