C++ Rusia: si ndodhi

Nëse në fillim të lojës ju thoni se ka kodin C++ të varur në mur, atëherë në fund ai është i detyruar t'ju qëllojë në këmbë.

Bjarne Stroustrup

Nga 31 tetori deri më 1 nëntor, në Shën Petersburg u mbajt konferenca C++ Russia Piter - një nga konferencat e programimit në shkallë të gjerë në Rusi, organizuar nga JUG Ru Group. Folësit e ftuar përfshijnë anëtarë të Komitetit të Standardeve të C++, folës të CppCon, autorë të librave të O'Reilly dhe mbajtës të projekteve si LLVM, libc++ dhe Boost. Konferenca synon zhvilluesit me përvojë të C++ të cilët dëshirojnë të thellojnë ekspertizën e tyre dhe të shkëmbejnë përvoja në komunikimin e drejtpërdrejtë. Studentët, studentët e diplomuar dhe mësuesit e universitetit u ofrohen zbritje shumë të këndshme.

Edicioni i Moskës i konferencës do të jetë i disponueshëm për t'u vizituar që në prill të vitit të ardhshëm, por ndërkohë studentët tanë do t'ju tregojnë se çfarë gjërash interesante mësuan në ngjarjen e fundit. 

C++ Rusia: si ndodhi

Foto nga album konferencash

Rreth nesh

Dy studentë nga Shkolla e Lartë Ekonomike e Universitetit Kombëtar të Kërkimit - Shën Petersburg punuan në këtë post:

  • Liza Vasilenko është një studente universitare e vitit të 4-të që studion Gjuhët e Programimit si pjesë e programit të Matematikës së Aplikuar dhe Shkencave Kompjuterike. Pasi u njoha me gjuhën C++ në vitin tim të parë në universitet, më pas fitova përvojë duke punuar me të përmes praktikave në industri. Pasioni për gjuhët e programimit në përgjithësi dhe programimin funksional në veçanti la gjurmë në zgjedhjen e raporteve në konferencë.
  • Danya Smirnov është studente e vitit të parë të programit master "Programimi dhe analiza e të dhënave". Kur isha ende në shkollë, shkrova problemet e olimpiadës në C++, dhe më pas ndodhi disi që gjuha të dilte vazhdimisht në aktivitetet edukative dhe përfundimisht të bëhej gjuha kryesore e punës. Vendosa të marr pjesë në konferencë për të përmirësuar njohuritë e mia dhe gjithashtu për të mësuar rreth mundësive të reja.

Në buletinin e lajmeve, udhëheqja e fakultetit shpesh ndan informacione rreth ngjarjeve arsimore që lidhen me specialitetin tonë. Në shtator pamë informacione për C++ Rusia dhe vendosëm të regjistroheshim si dëgjues. Kjo është përvoja jonë e parë e pjesëmarrjes në konferenca të tilla.

Struktura e konferencës

  • Raportet

Gjatë dy ditëve, ekspertët lexuan 30 raporte, duke mbuluar shumë tema të nxehta: përdorime të zgjuara të veçorive gjuhësore për të zgjidhur problemet e aplikuara, përditësime të ardhshme gjuhësore në lidhje me standardin e ri, kompromise në dizajnin C++ dhe masa paraprake kur punoni me pasojat e tyre, shembuj të arkitekturës interesante të projektit, si dhe disa detaje nën kapak të infrastrukturës gjuhësore. Tre shfaqje u zhvilluan njëkohësisht, më shpesh dy në rusisht dhe një në anglisht.

  • Zonat e diskutimit

Pas fjalimit, të gjitha pyetjet e pabëra dhe diskutimet e papërfunduara u transferuan në zona të caktuara posaçërisht për komunikim me folësit, të pajisura me tabela shënjuese. Një mënyrë e mirë për të larguar pushimin midis fjalimeve me një bisedë të këndshme.

  • Biseda rrufe dhe diskutime joformale

Nëse dëshironi të jepni një raport të shkurtër, mund të regjistroheni në tabelën e bardhë për Bisedën e Mbrëmjes së Rrufesë dhe të merrni pesë minuta kohë për të folur për çdo gjë mbi temën e konferencës. Për shembull, një prezantim i shpejtë i dezinfektuesve për C++ (për disa ishte i ri) ose një histori rreth një defekti në gjenerimin e valëve sinusale që mund të dëgjohet vetëm, por nuk mund të shihet.

Një tjetër format është paneli i diskutimit "Me një komision zemër për zemër". Në skenë janë disa anëtarë të komitetit të standardizimit, në projektor është një oxhak (zyrtarisht - për të krijuar një atmosferë të sinqertë, por arsyeja "sepse GJITHÇKA ËSHTË NË ZJARR" duket më qesharake), pyetje rreth standardit dhe vizionit të përgjithshëm të C++ , pa diskutime të nxehta teknike dhe holiwars. Doli se komiteti përmban gjithashtu njerëz të gjallë që mund të mos jenë plotësisht të sigurt për diçka ose mund të mos dinë diçka.

Për tifozët e holivarëve, ngjarja e tretë mbeti në këtë rast - seanca e BOF "Shko kundër C++". Marrim një dashnor Go, një dashnor C++, para fillimit të seancës ata së bashku përgatisin 100500 sllajde për një temë (si problemet me paketat në C++ ose mungesa e gjenerikës në Go), dhe më pas ata bëjnë një diskutim të gjallë mes tyre dhe me audiencën, dhe audienca përpiqet të kuptojë dy këndvështrime njëherësh. Nëse një holivar fillon jashtë kontekstit, moderatori ndërhyn dhe pajton palët. Ky format është problematik: disa orë pas fillimit, vetëm gjysma e diapozitivëve u përfunduan. Fundi duhej të përshpejtohej shumë.

  • Partneri qëndron

Partnerët e konferencës ishin të përfaqësuar në salla - në stenda ata folën për projektet aktuale, ofruan praktikë dhe punësim, mbajtën kuize dhe konkurse të vogla, si dhe hodhën çmime të këndshme. Në të njëjtën kohë, disa kompani madje ofruan të kalonin fazat fillestare të intervistave, të cilat mund të ishin të dobishme për ata që vinin jo vetëm për të dëgjuar raporte.

Detajet teknike të raporteve

Dëgjuam raporte të dyja ditët. Ndonjëherë ishte e vështirë të zgjidhej një raport nga ato paralele - ne ramë dakord të ndaheshim dhe të shkëmbenim njohuritë e marra gjatë pushimeve. E megjithatë, duket se shumë gjëra janë lënë jashtë. Këtu do të donim të flasim për përmbajtjen e disa prej raporteve që na duken më interesante

Përjashtimet në C++ përmes prizmit të optimizimeve të përpiluesit, Roman Rusyaev

C++ Rusia: si ndodhi
Rrëshqitje nga презентации

Siç sugjeron titulli, Roman shikoi punën me përjashtime duke përdorur LLVM si shembull. Në të njëjtën kohë, për ata që nuk përdorin Clang në punën e tyre, raporti mund të japë ende një ide se si mund të optimizohet kodi. Kjo ndodh sepse zhvilluesit e përpiluesve dhe bibliotekave standarde përkatëse komunikojnë me njëri-tjetrin dhe shumë zgjidhje të suksesshme mund të përkojnë.

Pra, për të trajtuar një përjashtim, ju duhet të bëni shumë gjëra: telefononi kodin e trajtimit (nëse ka) ose burime të lira në nivelin aktual dhe rrotulloni pirgun më lart. E gjithë kjo çon në faktin se përpiluesi shton udhëzime shtesë për thirrjet që potencialisht hedhin përjashtime. Prandaj, nëse përjashtimi nuk ngrihet në të vërtetë, programi do të vazhdojë të kryejë veprime të panevojshme. Për të reduktuar disi shpenzimet e përgjithshme, LLVM ka disa heuristika për përcaktimin e situatave ku kodi i trajtimit të përjashtimeve nuk ka nevojë të shtohet ose numri i udhëzimeve "shtesë" mund të reduktohet.

Folësi shqyrton rreth një duzinë prej tyre dhe tregon si situatat ku ato ndihmojnë në përshpejtimin e ekzekutimit të programit, dhe ato ku këto metoda nuk janë të zbatueshme.

Kështu, Roman Rusyaev i çon studentët në përfundimin se kodi që përmban trajtimin e përjashtimeve nuk mund të ekzekutohet gjithmonë me zero para dhe jep këshillat e mëposhtme:

  • gjatë zhvillimit të bibliotekave, ia vlen të braktisim përjashtimet në parim;
  • nëse nevojiten akoma përjashtime, atëherë sa herë që është e mundur ia vlen të shtoni modifikues noexcept (dhe const) kudo në mënyrë që përpiluesi të mund të optimizojë sa më shumë që të jetë e mundur.

Në përgjithësi, folësi konfirmoi pikëpamjen se përjashtimet përdoren më së miri në minimum ose të braktisen fare.

Sllajdet e raportit janë në dispozicion në lidhjen e mëposhtme: ["Përjashtimet C++ përmes objektivit të optimizimeve të përpiluesit LLVM"]

Gjeneratorë, korutina dhe ëmbëlsi të tjera që shpalosin trurin, Adi Shavit

C++ Rusia: si ndodhi
Rrëshqitje nga презентации

Një nga raportet e shumta në këtë konferencë kushtuar inovacioneve në C++20 ishte i paharrueshëm jo vetëm për prezantimin e tij plot ngjyra, por edhe për identifikimin e qartë të problemeve ekzistuese me logjikën e përpunimit të koleksionit (për ciklin, kthimet e thirrjeve).

Adi Shavit thekson sa vijon: metodat e disponueshme aktualisht kalojnë në të gjithë koleksionin dhe nuk ofrojnë akses në ndonjë gjendje të ndërmjetme të brendshme (ose e bëjnë në rastin e kthimeve të thirrjeve, por me një numër të madh efektesh anësore të pakëndshme, si për shembull Callback Hell) . Duket se ka përsëritës, por edhe me ta gjithçka nuk është aq e qetë: nuk ka pika të përbashkëta hyrje-dalje (fillimi → fundi kundrejt rbegin → rend dhe kështu me radhë), nuk është e qartë se për sa kohë do të përsërisim? Duke filluar me C++20, këto probleme janë zgjidhur!

Opsioni i parë: vargjet. Duke mbështjellë përsëritësit, ne marrim një ndërfaqe të përbashkët për fillimin dhe fundin e një përsëritjeje, dhe gjithashtu marrim aftësinë për të kompozuar. E gjithë kjo e bën të lehtë ndërtimin e tubacioneve të plota të përpunimit të të dhënave. Por jo gjithçka është aq e qetë: një pjesë e logjikës së llogaritjes ndodhet brenda zbatimit të një përsëritësi specifik, i cili mund të komplikojë kodin për të kuptuar dhe korrigjuar.

C++ Rusia: si ndodhi
Rrëshqitje nga презентации

Epo, për këtë rast, C++20 shtoi korutina (funksione sjellja e të cilëve është e ngjashme me gjeneratorët në Python): ekzekutimi mund të shtyhet duke kthyer një vlerë aktuale duke ruajtur një gjendje të ndërmjetme. Kështu, ne arrijmë jo vetëm të punojmë me të dhënat siç duken, por edhe të kapsulojmë të gjithë logjikën brenda një korutine specifike.

Por ka një mizë në vaj: për momentin ato mbështeten vetëm pjesërisht nga përpiluesit ekzistues, dhe gjithashtu nuk zbatohen aq mirë sa do të donim: për shembull, nuk ia vlen të përdorni ende referenca dhe objekte të përkohshme në korutina. Plus, ka disa kufizime se çfarë mund të jenë korutina, dhe funksionet constexpr, konstruktorët/destruktorët dhe kryesore nuk përfshihen në këtë listë.

Kështu, korutinat zgjidhin një pjesë të konsiderueshme të problemeve me thjeshtësinë e logjikës së përpunimit të të dhënave, por zbatimet e tyre aktuale kërkojnë përmirësim.

materialet:

Truket C++ nga Yandex.Taxi, Anton Polukhin

Në aktivitetet e mia profesionale, ndonjëherë më duhet të zbatoj gjëra thjesht ndihmëse: një mbështjellës midis ndërfaqes së brendshme dhe API-së së një biblioteke, regjistrimi ose analizimi. Në këtë rast, zakonisht nuk ka nevojë për ndonjë optimizim shtesë. Por, çka nëse këta komponentë përdoren në disa nga shërbimet më të njohura në RuNet? Në një situatë të tillë, do t'ju duhet të përpunoni vetëm terabajt në orë shkrime! Atëherë çdo milisekondë vlen dhe për këtë arsye ju duhet të drejtoheni në truket e ndryshme - foli Anton Polukhin për to.

Ndoshta shembulli më interesant ishte zbatimi i modelit 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_; 
};

Në këtë shembull, së pari dua të heq qafe skedarët e kokës së bibliotekave të jashtme - kjo do të përpilohet më shpejt, dhe ju mund të mbroheni nga konfliktet e mundshme të emrit dhe gabime të tjera të ngjashme. 

Në rregull, ne kaluam #include te skedari .cpp: na duhet një deklarim përpara i API-së së mbështjellë, si dhe std::unique_ptr. Tani kemi ndarje dinamike dhe gjëra të tjera të pakëndshme si të dhënat e shpërndara nëpër një mori të dhënash dhe garanci të reduktuara. std::aligned_storage mund të ndihmojë me gjithë këtë. 

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

Problemi i vetëm: duhet të specifikojmë madhësinë dhe shtrirjen për çdo mbështjellës - le të bëjmë shabllonin tonë pimpl me parametrat , të përdorim disa vlera arbitrare dhe t'i shtojmë një kontroll destruktorit që kemi hamendësuar gjithçka siç duhet. : 

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

Meqenëse T është përcaktuar tashmë gjatë përpunimit të destruktorit, ky kod do të analizohet saktë dhe në fazën e përpilimit do të nxjerrë madhësinë e kërkuar dhe vlerat e shtrirjes që duhet të futen si gabime. Kështu, me koston e një ekzekutimi shtesë të përpilimit, ne heqim qafe shpërndarjen dinamike të klasave të mbështjella, fshehim API-në në një skedar .cpp me zbatimin dhe gjithashtu marrim një dizajn që është më i përshtatshëm për ruajtje nga procesori.

Regjistrimi dhe analizimi dukeshin më pak mbresëlënës dhe për këtë arsye nuk do të përmenden në këtë përmbledhje.

Sllajdet e raportit janë në dispozicion në lidhjen e mëposhtme: ["Truket C++ nga Taxi"]

Teknika moderne për mbajtjen e kodit tuaj DRY, Björn Fahler

Në këtë bisedë, Björn Fahler tregon disa mënyra të ndryshme për të luftuar të metën stilistike të kontrolleve të përsëritura të gjendjes:

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

Tingëllon e njohur? Duke përdorur disa teknika të fuqishme C++ të prezantuara në standardet e fundit, ju mund të zbatoni në mënyrë elegante të njëjtin funksionalitet pa asnjë ndëshkim të performancës. Krahaso:   

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

Për të trajtuar një numër të pafiksuar kontrollesh, duhet menjëherë të përdorni shabllone variadike dhe shprehje palosëse. Le të supozojmë se duam të kontrollojmë barazinë e disa variablave me elementin state_type të enum. Gjëja e parë që ju vjen në mendje është të shkruani një funksion ndihmës është_any_of:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

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

Ky rezultat i ndërmjetëm është zhgënjyes. Deri tani kodi nuk po bëhet më i lexueshëm:

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

Parametrat e shabllonit jo të tipit do të ndihmojnë në përmirësimin e situatës pak. Me ndihmën e tyre, ne do të transferojmë elementët e numërueshëm të enumit në listën e parametrave të shabllonit: 

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

Duke përdorur automatik në një parametër shabllon jo-tip (C++17), qasja thjesht përgjithësohet me krahasimet jo vetëm me elementët e tipit_state, por edhe me llojet primitive që mund të përdoren si parametra të shabllonit jo tip:


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

Nëpërmjet këtyre përmirësimeve të njëpasnjëshme, arrihet sintaksa e dëshiruar e rrjedhshme për kontrollet:


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

Në këtë shembull, udhëzuesi i zbritjes shërben për të sugjeruar parametrat e dëshiruar të modelit të strukturës për përpiluesin, i cili njeh llojet e argumenteve të konstruktorit. 

Më tej - më interesante. Bjorn mëson se si të përgjithësohet kodi që rezulton për operatorët e krahasimit përtej ==, dhe më pas për operacione arbitrare. Gjatë rrugës, veçori të tilla si atributi no_unique_address (C++20) dhe parametrat e shabllonit në funksionet lambda (C++20) shpjegohen duke përdorur shembuj të përdorimit. (Po, tani sintaksa lambda është edhe më e lehtë për t'u mbajtur mend - këto janë katër palë kllapa të njëpasnjëshme të të gjitha llojeve.) Zgjidhja përfundimtare duke përdorur funksionet si detaje konstruktori më ngroh vërtet shpirtin, për të mos përmendur shprehjen tuple në traditat më të mira të lambda llogaritje.

Në fund, mos harroni ta lustroni:

  • Mos harroni se lambdat janë constexpr falas; 
  • Le të shtojmë përcjelljen e përsosur dhe të shohim sintaksën e saj të shëmtuar në lidhje me paketën e parametrave në mbylljen lambda;
  • Le t'i japim kompajlerit më shumë mundësi për optimizime me nove të kushtëzuara; 
  • Le të kujdesemi për daljen e gabimeve më të kuptueshme në shabllone falë vlerave të qarta të kthimit të lambdas. Kjo do ta detyrojë përpiluesin të bëjë më shumë kontrolle përpara se funksioni i shabllonit të thirret në të vërtetë - në fazën e kontrollit të tipit. 

Për detaje, ju lutemi referojuni materialeve të ligjëratës: 

Përshtypjet tona

Pjesëmarrja jonë e parë në C++ Rusia ishte e paharrueshme për intensitetin e saj. Kam marrë përshtypjen e C++ Rusisë si një ngjarje e sinqertë, ku linja midis trajnimit dhe komunikimit live është pothuajse e padukshme. Gjithçka, nga gjendja shpirtërore e folësve deri te konkurset nga partnerët e ngjarjes, është e favorshme për diskutime të nxehta. Përmbajtja e konferencës, e përbërë nga raporte, mbulon një gamë mjaft të gjerë temash, duke përfshirë risitë C++, studimet e rasteve të projekteve të mëdha dhe konsideratat arkitekturore ideologjike. Por do të ishte e padrejtë të injorohej komponenti social i ngjarjes, i cili ndihmon në kapërcimin e barrierave gjuhësore në lidhje jo vetëm me C++.

Falenderojmë organizatorët e konferencës për mundësinë për të marrë pjesë në një ngjarje të tillë!
Ju mund të keni parë postimin e organizatorëve për të kaluarën, të tashmen dhe të ardhmen e C++ Rusisë në blogun JUG Ru.

Faleminderit për leximin dhe shpresojmë që ritregimi i ngjarjeve të ishte i dobishëm!

Burimi: www.habr.com

Shto një koment