C++ Rusko: jak to probíhalo

Pokud na začátku hry řeknete, že na zdi visí kód v C++, pak na konci by vám měl rozhodně vystřelit do nohy.

Bjarne Stroustrup

Od 31. října do 1. listopadu se v Petrohradě konala konference C++ Russia Piter – jedna z nejvýznamnějších konferencí o programování v Rusku, organizovaná JUG Ru Group. Mezi pozvanými řečníky byli členové výboru pro standardizaci C++, přednášející z CppCon, autoři knih z nakladatelství O’Reilly a také udržovatelé projektů jako LLVM, libc++ a Boost. Konference je zaměřena na zkušené vývojáře v C++, kteří chtějí prohloubit svou expertizu a sdílet zkušenosti v živém kontaktu. Studentům, postgraduálním studentům a učitelům univerzit se nabízejí velmi příjemné slevy.

Moskevské vydání konference bude možné navštívit už v dubnu příštího roku, zatímco naši studenti nám poví, co zajímavého se dozvěděli na proběhlé akci. 

C++ Rusko: jak to probíhalo

Foto z alba konference

O nás

Na tomto příspěvku spolupracovali dva studenti HSE – Petrohrad:

  • Liza Vasilenko – studentka 4. ročníku bakalářského studia, studující obor "Programovací jazyky" v rámci programu "Aplikovaná matematika a informatika". Seznámila se s jazykem C++ na prvním ročníku univerzity, poté získala zkušenosti s ním na stážích v průmyslu. Zájem o programovací jazyky obecně a funkcionální programování konkrétně ovlivnilo výběr přednášek na konferenci.
  • Danya Smirnov – student 1. ročníku magisterského studia "Programování a analýza dat". Ještě na škole psal na C++ olympijské úkoly, a pak se nějak stalo, že se jazyk neustále objevoval v jeho studijní činnosti a nakonec se stal jeho hlavním pracovním jazykem. Na konferenci se rozhodl zúčastnit, aby si vylepšil své znalosti a také se dozvěděl o nových možnostech.

V newsletteru fakultní vedení často sdílí informace o vzdělávacích akcích souvisejících s naší specializací. V září jsme uviděli informace o C++ Russia a rozhodli jsme se zaregistrovat jako posluchači. Je to naše první zkušenost s účastí na podobných konferencích.

Struktura konference

  • Prezentace

Během dvou dnů experti přečetli 30 zpráv, které pokryly mnoho aktuálních témat: důvtipné aplikace funkcí jazyka pro řešení praktických úloh, nadcházející aktualizace jazyka v důsledku nového standardu, kompromisy při návrhu C++ a opatření k opatrnosti při práci s jejich následky, příklady zajímavé architektury projektů a také některé technické detaily infrastruktury jazyka. Současně probíhala tři vystoupení, nejčastěji dvě v ruštině a jedno v angličtině.

  • Diskusní zóny

Po vystoupení byly všechny nezodpovězené dotazy a nedokončené diskuse přesunuty do speciálně vyhrazených komunikačních zón s přednášejícími, vybavenými tabulemi. Skvělý způsob, jak strávit přestávku mezi vystoupeními příjemným rozhovorem.

  • Lightning Talks a neformální diskuse

Pokud máte chuť udělat krátkou přednášku, můžete se přihlásit na tabuli na večerní Lightning Talk a získat pět minut na vyprávění o čemkoli souvisejícím s konferencí. Například rychlé úvod do sanitizátorů pro C++ (pro některé bylo novinkou) nebo příběh o chybě v generaci sinusoidy, kterou lze slyšet, ale nevidět.

Další formát — panelová diskuse „S výborem o duších“. Na scéně — někteří členové standardizačního výboru, na projektoru — krb (oficiálně — pro vytvoření útulné atmosféry, ale důvod „protože VŠE JE V OHNI“ se zdá zábavnější), dotazy — o standardu a celkovém pohledu na C++, bez bouřlivých technických diskusí a vášní. Ukázalo se, že ve výboru také sedí živí lidé, kteří si mohou v něčem být ne zcela jisti nebo něco nevědět.

Pro milovníky vášní zůstávala třetí akce — BOF-sesie „Go versus C++“. Bereme milovníka Go, milovníka C++, před začátkem sezení společně připravují 100500 slidů na téma (např. problémy s balíčky v C++ nebo nedostatek generik v Go), a poté živě diskutují mezi sebou a s publikem, přičemž publikem se snaží pochopit obě perspektivy. Pokud se začne vášnivá diskuse mimo téma — moderátor zasahuje a smířuje strany. Tento formát se táhne: několik hodin po začátku bylo projednáno pouze půlka slidů. Konec se musel hodně urychlit.

  • Stánky partnerů

V halách byli představeni partneři konference — na stáncích představovali aktuální projekty, nabízeli stáže a zaměstnání, pořádali kvízy a malé soutěže, a také losovali příjemné ceny. Některé společnosti dokonce nabízely absolvování prvních fází pohovorů, což může být užitečné pro ty, kteří přišli nejen poslouchat přednášky.

Technické podrobnosti přednášek

Oba dny jsme poslouchali přednášky. Někdy bylo těžké vybrat si jednu přednášku z paralelně probíhajících – dohodli jsme se rozdělit a v přestávkách si vyměňovat nabyté znalosti. Přesto se zdá, že jsme mnohé zmeškali. Zde bychom chtěli vyprávět o obsahu některých přednášek, které se nám zdály nejzajímavější.

Výjimky v C++ skrze prizmu optimalizací kompilátorů, Roman Rusjaev

C++ Rusko: jak to probíhalo
Snímek z prezentaci

Jak je z názvu patrné, Roman se zabýval prací s výjimkami na příkladu LLVM. I pro ty, kteří ve své práci nepoužívají Clang, může mít přednáška stále určité užitečné informace o tom, jak může být kód potenciálně optimalizován. To tak je, protože vývojáři kompilátorů a příslušných standardních knihoven spolu komunikují a mnohá úspěšná řešení se mohou shodovat.

Pro zpracování výjimky je zapotřebí provést řadu kroků: zavolat kód zpracování (pokud existuje), nebo uvolnit prostředky na aktuální úrovni a rozbalit zásobník výše. To všechno vede k tomu, že kompilátor pro potenciální volání, které mohou vyvolat výjimky, přidává další instrukce. Proto i pokud výjimka nakonec nebude vyvolána, program stále provádí zbytečné kroky. Aby bylo možné snížit režijní náklady, existuje v LLVM několik heuristik určujících situace, kdy není potřeba přidávat kód pro zpracování výjimek, nebo lze snížit počet „zbytečných“ instrukcí.

Přednášející popisuje asi desítku z nich a ukazuje, jak situace, kde pomáhají urychlit provádění programu, tak ty, kde nejsou tyto metody použitelné.

Takto Roman Rusjaev přivádí posluchače k závěru, že kód obsahující práci s výjimkami rozhodně nelze vždy vykonávat s nulovými režijními náklady, a dává následující rady:

  • Při vývoji knihoven je dobré se zcela vzdát výjimek.
  • Pokud jsou výjimky nezbytné, je dobré všude přidávat modifikátory noexcept (a const), aby mohl kompilátor optimalizovat co nejvíce.

Celkově řečník potvrdil názor, že by se výjimky měly používat co nejméně, nebo se jich dokonce úplně vzdát.

Prezentace je k dispozici na odkaze: [„Výjimky C++ skrze optymalizace kompilátoru LLVM“]

Generátory, korutiny a další lahůdky pro přemetání mysli, Adi Shavit

C++ Rusko: jak to probíhalo
Snímek z prezentaci

Jedna z mnoha prezentací této konference zaměřená na novinky C++20 se zapsala do paměti nejen krásně zpracovanou prezentací, ale také jasným vymezením existujících problémů s logikou zpracování kolekcí (cyklus for, callbacky).

Adi Shavit vyzdvihuje následující: současné metody procházejí celou kolekcí a přitom neposkytují přístup k některému vnitřnímu mezistavu (nebo to dělají v případě callbacků, ale s mnoha nepříjemnými vedlejšími efekty, jako je Callback Hell). Může se zdát, že existují iterátory, ale ani s nimi není všechno tak jednoduché: neexistují společné bodové vstupy a výstupy (begin → end proti rbegin → rend atd.), není jasné, kolikrát vůbec budeme iterovat? Od C++20 se tyto problémy řeší!

První varianta: ranges. Díky obalování nad iterátory získáváme společné rozhraní pro začátek a konec iterace a také možnost kompozice. To vše usnadňuje budování plnohodnotných datových zpracovatelských potrubí. Ale ne všechno je tak hladké: část logiky výpočtů se nachází uvnitř implementace konkrétního iterátoru, což může ztížit čtení a ladění kódu.

C++ Rusko: jak to probíhalo
Snímek z prezentaci

Co se tedy v tomto případě v C++20 přidaly korutiny (funkce, jejichž chování je podobné generátorům v Pythonu): vykonávání může být odloženo, přičemž vracíme nějakou aktuální hodnotu a přitom uchováváme mezistav. To nám umožňuje nejen pracovat s daty, jak se objevují, ale také inkapsulovat celou logiku uvnitř konkrétní korutiny.

Ale je tu jeden háček: v současnosti jsou pouze částečně podporovány dostupnými kompilátory a také nejsou tak precizně implementovány, jak bychom si přáli: například v korutinách zatím není doporučeno používat reference a dočasné objekty. Navíc existují určitá omezení, co může být korutinami, a constexpr funkce, konstruktory/destruktory a také main do tohoto seznamu nepatří.

Tím pádem korutiny řeší značnou část problémů s jednoduchostí logiky zpracování dat, ale jejich aktuální implementace vyžadují vylepšení.

Materiály:

C++ triky z Yandex.Taxi, Anton Poluchin

Ve své profesní činnosti se občas musím zabývat čistě pomocnými věcmi: wrapperem mezi interním rozhraním a API nějaké knihovny, logováním nebo parsováním. Přitom obvykle není potřeba žádná další optimalizace. Ale co když jsou tyto komponenty používány v některých z nejpopulárnějších služeb v Ruském internetu? V takové situaci bude potřeba zpracovávat terabajty logů za hodinu! Každá milisekunda je cenná a proto se musíme uchýlit k různým trikům — o těchto trikech mluvil Anton Poluchin.

Snad nejzajímavějším příkladem byla implementace vzoru pointer-to-implementation (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 tomto příkladu se nejprve chceme zbavit hlavičkových souborů externích knihoven — tak bude kompilace rychlejší a můžeme se ochránit před možnými konflikty jmen a podobnými chybami. 

Dobře, přesunuli jsme #include do .cpp souboru: potřebujeme forward-declaration obaleného API a také std::unique_ptr. Teď máme dynamické alokace a další nepříjemné věci jako rozptýlená data v haldě a snížené záruky. Se vším tímto může pomoci 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_; 
};

Jediný problém: je třeba pro každý wrapper uvést velikost a zarovnání — uděláme náš pimpl šablonový s parametry , použijeme nějaké libovolné hodnoty a přidáme do destruktora kontrolu, že jsme všechno uhádli: 

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

Protože je destruktor T již definován, tento kód bude správně vyřešen i ve fázi kompilace a uvede potřebné hodnoty velikosti a zarovnání jako chyby. Takto se cenou jednoho dalšího spuštění kompilace zbavíme dynamické alokace obalových tříd, skryjeme API do .cpp souboru s implementací a získáme také konstrukci, která je lépe vhodná pro procesorové cache.

Záznam a analýza vypadají méně působivě, a proto v této recenzi nebudou zmíněny.

Prezentace je k dispozici na odkaze: [„C++ triky z Taksi“]

Moderní techniky, jak udržet váš kód DRY, Björn Fahller

V této prezentaci Björn Fahller ukazuje několik různých způsobů, jak se vypořádat s takovou stylistickou chybou, jako jsou opakované kontroly podmínek:

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

Zní povědomě? Pomocí několika mocných technik C++, které se objevily v nedávných standardech, lze elegantně realizovat stejnou funkčnost bez jakýchkoli ztrát na výkonu. Porovnejte:   

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

Pro zpracování proměnlivého počtu kontrol se nabízí použít variadic templates a fold expressions. Předpokládejme, že chceme zkontrolovat rovnost několika proměnných s prvkem enum state_type. První, co vás napadne, je napsat pomocnou funkci is_any_of:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

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

Takový mezivýsledek vyvolává zklamání. Zatím kód není čitelnější:

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

Trochu situaci vylepší non-type template parameters. S jejich pomocí перенeseme prvky enumerace enum do seznamu parametů šablony: 

template 
bool is_any_of(state_type t) { 
    return ((t == states) | ...); 
}
	
assert(is_any_of(state)); 

S použitím auto v ne-typovém parametru šablony (C++17) se přístup jednoduše zobecňuje na porovnání nejen s prvky state_type, ale i s primitivními typy, které lze použít jako non-type template parameters:


template 
bool is_any_of(const T& t) {
    return ((t == alternatives) | ...);
}

Cílenými sekvenčními zlepšeními dosahujeme požadované plynulé syntaxe pro kontroly:


template 
struct any_of : private std::tuple { 
// nebudeme se s tím trápit a zdědíme konstruktory z tuple 
        using std::tuple::tuple;
        template 
        bool operator ==(const T& t) const {
                return std::apply(
                        [&t](const auto& ... ts) {
                                return ((ts == t) || ...);
                        },
                        static_cast<const std::tuple&>(*this));
        }
};

template 
any_of(Ts ...) -> any_of;

assert(any_of(IDLE, DISCONNECTING, DISCONNECTED) == state);

V tomto příkladu slouží vodítko pro dedukci k naznačení požadovaných šablonových parametrů struktury kompilátoru, který zná typy argumentů konstruktora. 

Dál se to stává zajímavějším. Bjørn učí, jak zgeneralizovat vzniklý kód pro operátory porovnání kromě == a poté i pro libovolné operace. Mimochodem se na příkladu používání vysvětlují takové funkce jako no_unique_address attribute (C++20) a šablonové parametry v lambda funkcích (C++20). (Ano, teď je syntaxe lambd ještě snazší k zapamatování – jsou to čtyři po sobě jdoucí páry závorek všech typů.) Konečné řešení s využitím funkcí jako dílů konstruktoru mě osobně velmi hřeje u srdce, nemluvě o vyjádření tuple v nejlepších tradicích lambda kalkulu.

Na závěr nezapomeňte na finální úpravy:

  • Připomeňme si, že lambdy jsou constexpr zdarma; 
  • Přidáme perfektní přesměrování a podíváme se na jeho ošklivou syntaxi aplikovanou na parameter pack v uzávěrech lambd;
  • Poskytneme kompilátoru více možností pro optimalizace pomocí podmínkového noexcept; 
  • Postaráme se o srozumitelnější výstup chyb v šablonách díky explicitním návratovým hodnotám lambd. To přinutí kompilátor provádět více kontrol ještě před vlastním voláním šablonové funkce – ve fázi kontroly typů. 

Pro podrobnosti se obraťte na materiály výuky: 

Naše dojmy

Naše první účast na C++ Russia se zapíše jako velmi intenzivní. Vytvořil se dojem o C++ Russia jako o srdečné akci, kde je hranice mezi učením a živou komunikací téměř nepostřehnutelná. Vše, od nálady přednášejících po soutěže od partnerů akce, vybízí k bouřlivým diskuzím. Obsahová část konference, která spočívá v přednáškách, pokrývá poměrně široké spektrum témat včetně novinek C++, příkladů z praxe velkých projektů a ideologických architektonických úvah. Ale bylo by nespravedlivé nevenovat pozornost také sociální složce akce, která napomáhá překonání jazykových bariér nejen ve vztahu k C++.

Děkujeme organizátorům konference za možnost účastnit se takové události!
Příspěvek organizátorů o minulosti, přítomnosti a budoucnosti C++ Russia jste mohli vidět v blogu JUG Ru.

Děkujeme za přečtení a doufáme, že naše shrnutí událostí bylo užitečné!

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster