Hvis du i starten af stykket siger, at der hænger en C++-kode på væggen, så skal den uundgåeligt skyde dig i foden i slutningen.
Bjarne Stroustrup
Fra 31. oktober til 1. november fandt konferencen C++ Russia Piter sted i Sankt Petersborg – en af de største konferencer om programmering i Rusland, organiseret af JUG Ru Group. Blandt de inviterede talere er medlemmer af C++ standardiseringskomitéen, foredragsholdere fra CppCon, forfattere til bøger fra forlaget O’Reilly samt vedligeholdere af projekter som LLVM, libc++ og Boost. Konferencen henvender sig til erfarne C++-udviklere, der ønsker at uddybe deres ekspertise og udveksle erfaringer i levende samtaler. Studerende, ph.d.-studerende og universitetslærere får meget attraktive rabatter.
Den moskovitiske udgave af konferencen kan allerede besøges i april næste år, men indtil da vil vores studerende fortælle, hvad spændende de har lært på det nylige arrangement.

Foto fra
Om os
To studerende fra HSE – Sankt Petersborg har arbejdet med dette indlæg:
- Liza Vasilenko – studerende på 4. årgang af bacheloruddannelsen, der studerer 'Programmeringssprog' inden for programmet 'Anvendt matematik og databehandling'. Efter at have stiftet bekendtskab med C++ på første år af universitetet fik hun senere erfaring med det under praktikophold i industrien. Hendes interesse for programmeringssprog generelt og funktionel programmering særligt har præget hendes valg af foredrag på konferencen.
- Danya Smirnov – studerende på 1. årgang af kandidatuddannelsen 'Programmering og dataanalyse'. Allerede i gymnasiet løste han olympiske opgaver på C++, og siden er det blevet sådan, at sproget konstant er dukket op i hans studier og i sidste ende blev hans primære arbejdsredskab. Han besluttede at deltage i konferencen for at forbedre sine færdigheder og også lære om nye muligheder.
I nyhedsbrevet deler fakultetsledelsen ofte information om uddannelsesbegivenheder relateret til vores speciale. I september så vi information om C++ Russia og besluttede at registrere os som lyttere. Dette er vores første erfaring med at deltage i sådanne konferencer.
Konferencens struktur
Presentationer
Inden for to dage læste eksperterne 30 rapporter, der dækkede mange varme emner: kloge anvendelser af sprogets funktioner til at løse anvendte opgaver, kommende opdateringer af sproget i forbindelse med den nye standard, kompromiser i designet af C++ og forholdsregler ved arbejde med deres konsekvenser, eksempler på interessant projektarkitektur samt nogle baggrundsdetaljer om sprogets infrastruktur. Der foregik samtidig op til 3 præsentationer, oftest to på russisk og en på engelsk.
Diskussionszoner
Efter præsentationen blev alle ubesvarede spørgsmål og ufærdige diskussioner overført til særligt udvalgte kommunikationsområder med oplægsholdere, der var udstyret med whiteboards. En god måde at tilbringe pausen mellem præsentationerne med en behagelig samtale.
Lightning Talks og uformelle diskussioner
Hvis du har lyst til at holde et kort oplæg, kan du tilmelde dig på whiteboardet til aftenens Lightning Talk og få fem minutter til at tale om hvad som helst relateret til konferencens tema. For eksempel, en hurtig introduktion til sanitizers for C++ (for nogle viste sig at være ny viden) eller en historie om en bug i genereringen af sinuskurven, som kun kan høres, men ikke ses.
En anden form er paneldiskussionen "Med udvalget om sjæle". På scenen sidder nogle medlemmer af standardiseringskomitéen, på projektoren vises en pejs (officielt for at skabe en varm atmosfære, men årsagen "fordi ALT ER I FLAMMER" virker sjovere), spørgsmålene handler om standarden og det overordnede syn på C++, uden heftige tekniske diskussioner og stridigheder. Det viste sig, at der også sidder levende mennesker i komitéen, som kan være usikre på nogle punkter eller ikke vide noget.
For dem der elsker stridigheder, var der den tredje aktivitet - BOF-sessionen "Go mod C++". Vi tager en Go-entusiast, en C++-entusiast, og før sessionen forbereder de sammen 100500 dias om emnet (såsom problemer med pakker i C++ eller manglen på generics i Go), og derefter diskuterer de livligt med hinanden og salen, mens salen forsøger at forstå to synspunkter på én gang. Hvis der opstår en strid uden relevans, griber moderator ind og forliger parterne. Denne form forlænges: flere timer efter starten var kun halvdelen af diasene blevet gennemgået. Slutningen måtte fremskyndes betydeligt.
Partnerstande
I konferencens haller præsenteredes partnere — ved deres stande delte de information om aktuelle projekter, tilbød praktikpladser og jobmuligheder, lavede quizzer og små konkurrencer, samt trak lod om fine præmier. Nogle virksomheder tilbød endda at gennemføre de indledende faser af jobsamtaler, hvilket kan være nyttigt for dem, der kom for ikke kun at lytte til foredrag.
Tekniske detaljer om foredragene
Vi lyttede til foredrag begge dage. Nogle gange var det svært at vælge ét foredrag blandt dem, der kørte parallelt – vi blev enige om at dele os og udveksle de opnåede viden i pauserne. Selv med det, føles det som om, at meget er blevet overset. Her vil vi gerne fortælle om indholdet af nogle af de foredrag, der virkede mest interessante for os.
Undtagelser i C++ gennem linsen af compileroptimeringer, Roman Rusyaev

Slide fra
Som titlen antyder, undersøgte Roman arbejdet med undtagelser ved hjælp af LLVM. For dem der ikke bruger Clang i deres arbejde, kan foredraget stadig give en vis forståelse af, hvordan koden potentielt kan optimeres. Dette skyldes, at compilerudviklere og de tilsvarende standardbiblioteker kommunikerer indbyrdes, og mange vellykkede løsninger kan være ens.
For at behandle en undtagelse skal der udføres mange handlinger: kalde en håndteringskode (hvis det er muligt) eller frigøre ressourcer på det nuværende niveau og rulle stakken opad. Alt dette fører til, at compileren tilføjer ekstra instruktioner for potentielt undtagelsesudløsende opkald. Derfor, hvis en undtagelse faktisk ikke udløses, vil programmet stadig udføre unødvendige handlinger. For at mindske omkostningerne, har LLVM flere heuristikker til at identificere situationer, hvor kode til håndtering af undtagelser ikke skal tilføjes, eller hvor antallet af "unødvendige" instruktioner kan reduceres.
Foredragsholderen gennemgår omkring et dusin af dem og viser både de situationer, hvor de hjælper med at fremskynde programmets udførelse, og dem hvor disse metoder ikke er anvendelige.
Således konkluderer Roman Rusyaev for lytterne, at kode, som indeholder arbejde med undtagelser, langt fra altid kan køres med nul overhead, og giver følgende råd:
- Når der udvikles biblioteker, bør man principielt afstå fra undtagelser.
- Hvis undtagelser alligevel er nødvendige, er det en god idé at tilføje noexcept-modifikatorer (og const) overalt, så compileren kan optimere så meget som muligt.
Generelt bekræftede oplægsholderen, at undtagelser bedst bruges i mindst mulige omfang, eller man helt bør afstå fra dem.
Foredragets slides er tilgængelige via linket:
Generators, koroutiner og andre hjernevridende sødme, Adi Shavit

Slide fra
Et af mange foredrag på denne konference om nyhederne i C++20 skilte sig ud ikke kun med en farverig præsentation, men også med en klar beskrivelse af de eksisterende problemer med logikken ved behandling af samlinger (for-loop, callbacks).
Adi Shavit fremhæver følgende: De nuværende metoder gennemgår hele samlingen og giver ikke adgang til en indre mellemtilstand (eller gør det i tilfælde af callbacks, men med mange ubehagelige bivirkninger, som f.eks. Callback Hell). Det ville synes, at der er iteratorer, men selv med dem er det ikke så glat: der er ingen fælles start- og slutpunkter (begin → end mod rbegin → rend osv.), og det er uklart, hvor meget vi egentligt vil iterere? Fra og med C++20 løses disse problemer!
Den første mulighed: ranges. Gennem indpakningen ovenfor iteratorer får vi et fælles interface til begyndelse og slutning af iterationen, og vi får også mulighed for sammensætning. Alt dette gør det muligt let at opbygge komplette databehandlingspipelines. Men ikke alt er glat: noget af logikken i beregningerne findes inde i implementeringen af den specifikke iterator, hvilket kan komplicere koden for perception og fejlfinding.

Slide fra
Hvad angår dette, er der i C++20 tilføjet koroutiner (funktioner, hvis adfærd ligner generatorer i Python): udførelsen kan udsættes ved at returnere en aktuel værdi, samtidig med at den mellemtilstand bevares. På denne måde opnår vi ikke kun at arbejde med data, mens de opstår, men også indkapsler vi al logik indenfor den specifikke koroutine.
Men der er en skefuld tjære: i øjeblikket understøttes de kun delvist af de eksisterende compilere, og de implementeres ikke så pænt, som man kunne ønske: for eksempel bør man lige nu ikke bruge referencer og temporære objekter i coroutines. Desuden er der visse begrænsninger i forhold til, hvad der kan være coroutines, og constexpr-funktioner, konstruktører/destroyers samt main er ikke på denne liste.
Derfor løser coroutines en betydelig del af problemerne med enkelheden i databehandlingslogik, men deres nuværende implementeringer kræver forbedringer.
Materialer:
- Slides fra C++ Russia —
C++ tricks fra Yandex.Taxi, Anton Polukhin
I mit professionelle arbejde må jeg nogle gange implementere rent hjælpefunktioner: en wrapper mellem et internt interface og API'en for et bibliotek, logging eller parsing. Normalt er der ikke behov for yderligere optimering. Men hvad hvis disse komponenter bruges i nogle af de mest populære tjenester i Runet? I så fald skal der behandles terabyte af logs pr. time! Så hver millisekund tæller, og derfor må man ty til forskellige tricks — som Anton Polukhin fortalte om.
Et af de mest interessante eksempler var implementeringen af mønsteret 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_;
};I dette eksempel er det først ønskeligt at slippe af med headerfilerne fra eksterne biblioteker — på den måde vil kompileringen gå hurtigere, og man kan beskytte sig mod mulige navnekonflikter og andre lignende fejl.
Godt, vi flyttede #include til .cpp-filen: en forward-declaration af den indkapslede API er nødvendig, samt std::unique_ptr. Nu har vi dynamiske allokeringer og andre ubehagelige ting som spredte data i bunken og nedsatte garantier. Alt dette kan std::aligned_storage hjælpe med.
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_;
};Det eneste problem: man skal angive størrelse og justering for hver wrapper — vi vil gøre vores pimpl skabelonbaseret med parametrene , bruge nogle vilkårlige værdier og tilføje en kontrol i destruktøren for at sikre, at vi har ramt rigtigt:
~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"
);
}Da T-destruktøren allerede er defineret under behandlingen, vil denne kode blive korrekt behandlet, og der opnås fejlmeddelelser på kompileringstidspunktet med de nødvendige værdier for størrelse og justeringer. På den måde kan vi med prisen for en ekstra kompilering undgå den dynamiske allokering af indpakkede klasser, skjule API'en i .cpp-filen med implementeringen samt opnå en mere cache-venlig struktur.
Logning og parsing virker mindre imponerende, og derfor vil de ikke blive nævnt i denne oversigt.
Foredragets slides er tilgængelige via linket:
Moderne teknikker til at holde din kode DRY, Björn Fahller
I denne præsentation viser Björn Fahller flere forskellige måder at tackle et stilistisk problem som gentagne betingelsestjek:
assert(a == IDLE || a == CONNECTED || a == DISCONNECTED);Kender du det? Ved at bruge et par kraftfulde C++ teknikker, der er dukket op i de seneste standarder, kan vi elegant implementere den samme funktionalitet uden selv de mindste præstationsomkostninger. Sammenlign:
assert(a == any_of(IDLE, CONNECTED, DISCONNECTED));Til behandling af et ubegribelig antal tjek synger variadic templates og fold expressions. Antag, at vi ønsker at kontrollere lighed mellem flere variable og et enum-element state_type. Det første, der kommer til at tænke på, er at skrive en hjælpefunktion is_any_of:
enum state_type { IDLE, CONNECTED, DISCONNECTED };
template
bool is_any_of(state_type s, const Ts& ... ts) {
return ((s == ts) || ...);
}
Denne mellemliggende resultat er skuffende. Indtil videre bliver koden ikke mere læsbar:
assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED)); Lidt justeringer med non-type template parameters kan hjælpe situationen. Med dem kan vi overføre de enum-elementer til skabelonparameterlisten:
template
bool is_any_of(state_type t) {
return ((t == states) | ...);
}
assert(is_any_of(state)); Ved hjælp af auto i et non-type skabelonparameter (C++17) kan metoden enkelt generaliseres til sammenligninger med ikke blot state_type-elementer, men også primitive typer, der kan bruges som non-type skabelonparametre:
template
bool is_any_of(const T& t) {
return ((t == alternatives) | ...);
}Gennem sådanne sekventielle forbedringer opnås den ønskede flydende syntaks til kontroller:
template
struct any_of : private std::tuple {
// lad os være dovne og arve konstruktørerne fra 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);
I dette eksempel bruges deductionsvejledningen til at antyde de ønskede skabelonparametre til strukturen for compileren, som kender typen af konstruktørens argumenter.
Det bliver mere interessant. Bjørn lærer at generalisere den resulterende kode til sammenligningsoperatorer udover ==, og derefter til vilkårlige operationer. Undervejs forklares funktioner som no_unique_address-attributten (C++20) og skabelonparametre i lambda-funktioner (C++20) med eksempler. (Ja, nu er syntaksen for lambdaer endnu lettere at huske – det er fire på hinanden følgende par parenteser af alle slags.) Den endelige løsning, der bruger funktioner som konstruktorens detaljer, varmer virkelig mit hjerte, for ikke at tale om at udtrykke tuple i bedste tradition for lambda-kalkyle.
Til slut glemmer vi ikke at give det et pift:
- Lad os huske på, at lambdaer er constexpr gratis;
- Vi tilføjer perfect forwarding og ser på dens grimme syntaks i forhold til parameter pack i lambda-bindings;
- Vi giver kompilatoren flere muligheder for optimeringer med conditional noexcept;
- Vi sørger for mere gennemskuelige fejludskrivninger i skabeloner ved hjælp af eksplicit return-værdier fra lambdaer. Dette tvinger kompilatoren til at udføre flere tjek inden selve kaldet til skabelonfunktionen – på typekontrolstadiet.
For detaljer, henvises der til forelæsningsmaterialet:
- Præsentationsslides:
Vores indtryk
Vores første deltagelse i C++ Russia vil blive husket for sin intensitet. Det gav indtryk af C++ Russia som et hjertevarmt arrangement, hvor grænsen mellem læring og levende kommunikation næsten ikke er mærkbar. Alt, fra stemningen blandt oplægsholderne til konkurrencerne fra arrangementets partnere, fremmer livlige diskussioner. Den faglige del af konferencen, der omfatter oplæg, dækker et ret bredt spektrum af emner, herunder nyskabelser inden for C++, eksempler fra store projekter og ideologiske arkitektoniske overvejelser. Men det ville være uretfærdigt at overse den sociale dimension af arrangementet, som bidrager til at overvinde sprogbarrierer i forhold til ikke kun C++.
Vi takker arrangørerne af konferencen for muligheden for at deltage i et så stort event!
Indlægget fra arrangørerne om fortiden, nutiden og fremtiden for C++ Russia har I kunnet se .
Tak fordi du læste med, og vi håber, at vores opsummering af begivenhederne har været nyttig!
Kilde: habr.com
