C++ Rusia: si ishte kjo

Nëse në fillim të shfaqjes thoni se në mur ka një kod në C++, atëherë deri në fund ai patjetër do t'ju godasë në këmbë.

Bjarne Stroustrup

Nga 31 tetori deri mĂ« 1 nĂ«ntor, nĂ« ShĂ«n Petersburg u zhvillua konferenca C++ Russia Piter – njĂ« nga konferencat mĂ« tĂ« mĂ«dha pĂ«r programim nĂ« Rusi, e organizuar nga JUG Ru Group. NdĂ«r folĂ«sit e ftuar ishin anĂ«tarĂ« tĂ« komitetit pĂ«r standardizimin e C++, folĂ«s nga CppCon, autorĂ« libresh nga shtĂ«pia botuese O’Reilly, si dhe mbajtĂ«s tĂ« projekteve si LLVM, libc++ dhe Boost. Konferenca Ă«shtĂ« e orientuar pĂ«r zhvilluesit e pĂ«rvojshĂ«m nĂ« C++, tĂ« cilĂ«t dĂ«shirojnĂ« tĂ« thellojnĂ« ekspertizĂ«n e tyre dhe tĂ« ndajnĂ« pĂ«rvojĂ«n nĂ« komunikim tĂ« drejtpĂ«rdrejtĂ«. StudentĂ«t, doktorantĂ«t dhe mĂ«suesit e universiteteve pĂ«rfitojnĂ« nga ulje shumĂ« tĂ« kĂ«ndshme.

Botimi i Moskës i konferencës do të mbahet në prill të vitit të ardhshëm, ndërsa për momentin studentët tanë do të tregojnë se çfarë interesante mësuan në këtë ngjarje. 

C++ Rusia: si ishte kjo

Foto nga albumi i konferencës

Rreth nesh

PĂ«r kĂ«tĂ« postim punuan dy studentĂ« tĂ« NIU HSE – ShĂ«n Petersburg:

  • Liza Vasiljenko – student i vitit tĂ« katĂ«rt nĂ« programin e bachelor-it, duke studiuar fushĂ«n "Gjuhtet e Programimit" nĂ« programin "Matematika e Aplikuar dhe Informatika". Duke u njohur me gjuhĂ«n C++ nĂ« vitin e parĂ« tĂ« universitetit, mĂ« pas fitoi pĂ«rvojĂ« pune me tĂ« nĂ« praktika nĂ« industri. Pasioni pĂ«r gjuhĂ«t e programimit nĂ« pĂ«rgjithĂ«si dhe programimin funksional nĂ« veçanti ndikoi nĂ« pĂ«rzgjedhjen e temave pĂ«r konferencĂ«.
  • Danya Smirnov – student i vitit tĂ« parĂ« nĂ« programin e magjistraturĂ«s "Programimi dhe Analiza e tĂ« DhĂ«nave". QĂ« nĂ« shkollĂ« shkruante probleme olimpike nĂ« C++, dhe mĂ« pas ndodhi qĂ« gjuha gjithmonĂ« shfaqej nĂ« aktivitetet e tij akademike dhe pĂ«rfundimisht u bĂ« gjuha kryesore e punĂ«s. Vendosi tĂ« marrĂ« pjesĂ« nĂ« konferencĂ« pĂ«r tĂ« rritur njohuritĂ« e tij, si dhe pĂ«r tĂ« mĂ«suar mbi mundĂ«sitĂ« e reja.

Në gazetën e fakultetit shpesh ndahen informacione mbi ngjarje edukative që lidhen me disiplinën tonë. Në shtator pamë informacion mbi C++ Russia dhe vendosëm të regjistroheshim si dëgjues. Ky është eksperienca jonë e parë në pjesëmarrjen në këtë lloj konferencash.

Struktura e konferencës

  • Referatet

Në dy ditë, ekspertët mbajtën 30 ligjërata, duke trajtuar shumë tema aktuale: aplikime të zgjuara të karakteristikave të gjuhës për zgjidhjen e detyrave praktike, azhurnimet e ardhshme të gjuhës në lidhje me standardin e ri, kompromiset gjatë dizajnimit të C++ dhe masat për kujdes gjatë punës me pasojat e tyre, shembuj të arkitekturës interesante të projekteve, si dhe disa detaje nën kapuçin e infrastrukturës së gjuhës. Në të njëjtën kohë zhvilloheshin 3 ligjërata, më shpesh dy në rusisht dhe një në anglisht.

  • Zonat e diskutimit

Pas ligjëratave, të gjitha pyetjet e pa bërë dhe diskutimet e papërfunduaraTransferoheshin në zona të veçanta të komunikimit me folësit, të pajisura me tabela shënuesish. Një mënyrë e mirë për të kaluar një pushim midis ligjëratave me një bisedë të këndshme.

  • LigjĂ«ratat e shpejta dhe diskutimet informale

NĂ«se dĂ«shiron tĂ« mbash njĂ« ligjĂ«ratĂ« tĂ« shkurtĂ«r – mund tĂ« regjistrohesh nĂ« tabelĂ«n e shĂ«nuesve pĂ«r LigjĂ«ratĂ«n e ShpejtĂ« tĂ« mbrĂ«mjes dhe tĂ« marrĂ«sh pesĂ« minuta pĂ«r tĂ« folur pĂ«r çfarĂ«do gjĂ«je nĂ« lidhje me temĂ«n e konferencĂ«s. PĂ«r shembull, njĂ« prezantim i shpejtĂ« mbi sanitizers pĂ«r C++ (pĂ«r disa ishte njĂ« risi) ose njĂ« histori pĂ«r njĂ« bug nĂ« gjenerimin e sinusoidĂ«s, qĂ« mund tĂ« dĂ«gjohet, por jo tĂ« shikohet.

Format tjetĂ«r – diskutimi nĂ« panel "Me komitetin pĂ«r shpirtin". NĂ« skenĂ« – disa anĂ«tarĂ« tĂ« komitetit pĂ«r standardizimin, nĂ« projektorin – njĂ« zjarri (zyrtarisht – pĂ«r tĂ« krijuar njĂ« atmosferĂ« tĂ« ngrohtĂ«, por arsyeja "sepse TË GJITHA JANË NË ZJAR" duket mĂ« qesharake), pyetjet – mbi standardin dhe vizionin e pĂ«rgjithshĂ«m pĂ«r C++, pa diskutime teknike tĂ« ashpra dhe holivare. Doli qĂ« nĂ« komitet janĂ« gjithashtu njerĂ«z tĂ« gjallĂ«, tĂ« cilĂ«t mund tĂ« kenĂ« tĂ« papĂ«rshtatshme ose tĂ« panjohura pĂ«r disa gjĂ«ra.

PĂ«r ata qĂ« pĂ«lqejnĂ« holivaret pĂ«r arsyet e duhura, mbetet ngjarja e tretĂ« – sesioni BOF "Go kundĂ«r C++". Marrim njĂ« adhurues tĂ« Go, njĂ« adhurues tĂ« C++, pĂ«rpara se tĂ« fillojnĂ« seancĂ«n ata pĂ«rgatitin sĂ« bashku 100500 slide pĂ«r temĂ«n (siç janĂ« problemet me paketat nĂ« C++ ose mungesa e gjenerikĂ«ve nĂ« Go), dhe mĂ« pas diskutojnĂ« me zĂ« tĂ« lartĂ« midis tyre dhe me sallĂ«n, ndĂ«rsa salla pĂ«rpiqet tĂ« kuptojĂ« dy kĂ«ndvĂ«shtrime tĂ« ndryshme. NĂ«se fillojnĂ« njĂ« holivar pa arsye – moderatorĂ«t ndĂ«rhyjnĂ« dhe pajtojnĂ« palĂ«t. Ky format zgjat: disa orĂ« pas fillimit, vetĂ«m gjysma e slide-ve u kalua. Pjesa e fundit u detyrua tĂ« pĂ«rshpejtohej.

  • Stendat e partnerĂ«ve

Në hollin e konferencës, partnerët e saj ishin të pranishëm - në stendat e tyre tregonin për projektet aktuale, ofronin mundësi stazhesh dhe punësimi, organizonin kuize dhe garat të vogla, si dhe shpërndanin çmime të këndshme. Disa kompani madje ofronin mundësinë për të kaluar etapat fillestare të intervistave, që mund të ishte e dobishme për ata që kishin ardhur jo vetëm për të dëgjuar referatet.

Detajet teknike të referateve

Ne dëgjuam referatet gjatë të dy ditëve. Ndonjëherë ishte e vështirë të zgjidhnim një referat për shkak të disa që zhvilloheshin paralelisht - u dakorduam të ndaheshim dhe të shkëmbenim njohuritë e fituara në pushime. Edhe kështu, duket se shumë gjëra janë humbur. Këtu do të dëshironim të flisnim për përmbajtjen e disa referateve që na dukeshin më interesante.

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

C++ Rusia: si ishte kjo
Slide nga prezentim

Siç tregohet nga titulli, Roman shqyrtoi punën me përjashtime duke përdorur LLVM si shembull. Për ata që nuk e përdorin Clang në punën e tyre, referati mund të ofrojë gjithashtu një pasqyrë mbi mënyrën se si mund të optimizohet kodin. Kjo është për shkak se zhvilluesit e kompajlerëve dhe bibliotekave standarte komunikojnë me njëri-tjetrin, dhe shumë zgjidhje të suksesshme mund të përputhen.

Pra, për të trajtuar një përjashtim kërkohen disa veprime: thirrja e kodit të trajtimit (nëse ekziston) ose çlirimi i burimeve në nivelin aktual dhe rrotullimi i stack-ut më lart. Të gjitha kjo çon në atë që për thirrjet që potencialisht mund të gjenerojnë përjashtime, kompajleri shton instruksione të tjera. Prandaj, nëse përjashtimi në fakt nuk do të ndodhte, programi do të vazhdojë të kryejë veprime të panevojshme. Për të reduktuar kostot, në LLVM ekzistojnë disa heuristika për identifikimin e situatave ku nuk është e nevojshme të shtohet kodi për trajtimin e përjashtimeve ose ku mund të reduktohet numri i instruksioneve "të tepërta".

Referuesi shqyrton rreth një duzinë të tillash dhe tregon si situatat ku ato ndihmojnë në përshpejtimin e ekzekutimit të programit, ashtu edhe ato ku këto metoda nuk janë të aplikueshme.

Kështu, Roman Rusyaev çon dëgjuesit në përfundimin se kodi që përmban punë me përjashtime, përherë nuk mund të ekzekutohet pa ndonjë kost të rëndësishme, dhe ofron këto këshilla:

  • nĂ« zhvillimin e bibliotekave, Ă«shtĂ« mĂ« mirĂ« tĂ« hiqni dorĂ« nga pĂ«rjashtimet nĂ« parim;
  • nĂ«se pĂ«rjashtimet janĂ« tĂ« nevojshme, atĂ«herĂ« sa mĂ« shumĂ« qĂ« tĂ« jetĂ« e mundur, duhen shtuar modifikatorĂ«t noexcept (dhe const), nĂ« mĂ«nyrĂ« qĂ« kompajleri tĂ« mund tĂ« optimizojĂ« sa mĂ« shumĂ«.

Në përgjithësi, referuesi konfirmoi mendimin se përjashtimet duhet të përdoren sa më pak ose madje të hiqen krejtësisht.

Prezantimet e referatit janë në dispozicion përmes lidhjes: ["Përjashtimet C++ përmes optimizimeve të kompajlerit LLVM"]

Generators, coroutines and other brain-unrolling sweetness, Adi Shavit

C++ Rusia: si ishte kjo
Slide nga prezentim

Një nga shumë referatet e kësaj konference, e dedikuar risive të C++20, mbeti në kujtesë jo vetëm për prezantimin e saj me ngjyra, por edhe për përshkrimin e qartë të problemeve ekzistuese me logjikën e trajtimit të koleksioneve (cikli for, callback-et).

Adi Shavit identifikon kĂ«to: metodat ekzistuese kalojnĂ« tĂ« gjithĂ« koleksionin dhe nuk ofrojnĂ« qasje nĂ« njĂ« gjendje interim tĂ« brendshme (ose e ofrojnĂ« nĂ« rastin e callback-Ă«ve, por me shumĂ« efekte anĂ«sore tĂ« padĂ«shiruara, si Hell-i i Callback). Duket se ka iteraatorĂ«, por as me ata, nuk Ă«shtĂ« gjithçka aq e thjeshtĂ«: nuk ka pika tĂ« zakonshme hyrjeje dhe daljeje (begin → end kundrejt rbegin → rend dhe kĂ«shtu me radhĂ«), nuk Ă«shtĂ« e qartĂ« se sa do tĂ« iteronim? Duke filluar nga C++20, kĂ«to probleme zgjidhen!

Opcioni i parë: ranges. Me ndihmën e mbushjes mbi iteraatorët, ne marrim një ndërfaqe të përbashkët për fillimin dhe përfundimin e iteracionit, si dhe mundësi për kompozim. Të gjitha këto lejojnë ndërtimin e linjave të plota të trajtimit të të dhënave. Por ashtu si çdo gjë, nuk është gjithçka e lehtë: një pjesë e logjikës së llogaritjeve ndodhet brenda implementimit të iteraatorit specifik, që mund ta komplikohet kodin për tu kuptuar dhe për debuggim.

C++ Rusia: si ishte kjo
Slide nga prezentim

Ajo që do të thoja, për këtë rast në C++20 janë shtuar korutina (funksione, sjellja e të cilave është e ngjashme me gjeneratorët në gjuhën Python): ekzekutimi mund të shtyhet, duke kthyer një vlerë aktuale me ruajtjen e gjendjes interim. Në këtë mënyrë, ne arrijmë jo vetëm të punojmë me të dhënat ndërsa ato shfaqen, por gjithashtu inkapsulojmë gjithë logjikën brenda korutinës specifike.

Poros është një mangësi: për momentin ato mbështeten vetëm pjesërisht nga kompilatorët ekzistues, si dhe janë implementuar jo aq saktë sa do të dëshirohej: për shembull, për momentin nuk duhet të përdoren lidhjet dhe objektet temporale në korutina. Për më tepër, ka disa kufizime në atë që mund të jetë korutina, dhe funksionet constexpr, konstruktorët/destroktorët, si dhe main nuk përfshihen në këtë listë.

Prandaj, korutinat zgjidhin një pjesë të konsiderueshme të problemeve me thjeshtësinë e logjikës së përpunimit të të dhënave, por implementimet e tyre aktuale kërkojnë përmirësime.

Materialet:

Truket e C++ nga Yandex.Taxi, Anton Polukhin

NĂ« profesionin tim ndonjĂ«herĂ« mĂ« duhet tĂ« realizoj gjĂ«ra thjesht ndihmĂ«se: njĂ« pĂ«rfaqĂ«sues midis ndĂ«rfaqes sĂ« brendshme dhe API-sĂ« sĂ« njĂ« biblioteke, regjistrimi ose analizimi. NĂ« kĂ«tĂ« rast, zakonisht nuk ka nevojĂ« pĂ«r optimizim tĂ« shtuar. Por çfarĂ« ndodh nĂ«se kĂ«to komponentĂ« pĂ«rdoren nĂ« disa nga shĂ«rbimet mĂ« tĂ« njohura nĂ« Runet? NĂ« njĂ« situatĂ« tĂ« tillĂ«, do tĂ« duhet tĂ« pĂ«rpunoni tera bajtĂ« çdo orĂ« vetĂ«m nga regjistrimet! AtĂ«herĂ« çdo milisekondĂ« ka rĂ«ndĂ«si dhe prandaj duhet tĂ« pĂ«rdoren trukĂ« tĂ« ndryshme — pĂ«r to fliste Anton Polukhin.

Mund të them se shembulli më interesant ishte implementimi 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, nĂ« fillim do tĂ« dĂ«shironim tĂ« heqnim pĂ«rfshirjet e skedarĂ«ve tĂ« biblioteka tĂ« jashtme — kĂ«shtu qĂ« kompilimi do tĂ« jetĂ« mĂ« i shpejtĂ« dhe mund tĂ« sigurohemi nga konflikte tĂ« mundshme emĂ«ror dhe gabime tĂ« tjera tĂ« ngjashme. 

Mirë, e transferuam #include në skedarin .cpp: duhet një forward-declaration të API-së të përfshirë, si dhe std::unique_ptr. Tani kemi alokime dinamike dhe gjëra të tjera të pakëndshme si të dhëna të shpërndara në grumbull dhe garanca të ulta. Me të gjitha këto mund të ndihmojë 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_; 
};

Problemi i vetĂ«m: duhet tĂ« specifikojmĂ« madhĂ«sinĂ« dhe pĂ«rmirĂ«simin pĂ«r çdo pĂ«rfaqĂ«sues — do ta bĂ«jmĂ« pimpl tonĂ« shabllon me parametra , ta pĂ«rdorim me disa vlera arbitrare dhe tĂ« shtojmĂ« nĂ« destruktorrin njĂ« kontroll qĂ« kemi gjithçka saktĂ«: 

~FastPimpl() noexcept { 
    validate(); 
    Ptr()->~T(); 
}

template 
static void validate() noexcept { 
    static_assert(
        Size == ActualSize, 
        "Madhësia dhe sizeof(T) nuk përputhen"
    ); 
    static_assert(
        Alignment == ActualAlignment, 
        "Përmirësimi dhe alignof(T) nuk përputhen"
    ); 
}

Pasi gjatë përpunimit të destruktori T tashmë është i definuar, ky kod do të kuptojë saktësisht dhe në fazën e kompilimit do të shfaqë vlerat e nevojshme të madhësisë dhe përmirësimit që duhet të përfshihen. Kështu, me një ekzekutim shtesë të kompilimit, ne shkyçim alokimin dinamik të klasave që përfshihen, fshehim API-në në skedarin .cpp me implementimin, si dhe marrim një strukturë më të përshtatshme për cache nga procesori.

Regjistrimi dhe analizimi duken më pak impresionuese, prandaj nuk do të përmenden në këtë përmbledhje.

Prezantimet e referatit janë në dispozicion përmes lidhjes: [«Truket e C++ nga Taxi»]

Teknikat moderne për të mbajtur kodin tuaj DRY, Björn Fahller

Në këtë prezantim, Björn Fahller tregon disa mënyra të ndryshme për të luftuar një gabim stilistik siç janë kontrollimet e përsëritura të kushteve:

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

E njohur? Duke përdorur disa teknika të fuqishme të C++, të cilat janë shfaqur në standardet e fundit, mund të implementoni me elegancë të njëjtën funksionalitet pa humbur asnjë performancë. Krahaso:   

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

PĂ«r tĂ« pĂ«rpunuar njĂ« numĂ«r tĂ« paqĂ«ndrueshĂ«m tĂ« kontrollimeve, pĂ«r tĂ« pĂ«rdorur template variadic dhe shprehjet e fold janĂ« tĂ« nevojshme. Le tĂ« supozojmĂ« se duam tĂ« kontrollojmĂ« barazimin e disa variablave me njĂ« element tĂ« enum state_type. E para qĂ« vjen nĂ« mend – tĂ« shkruajmĂ« njĂ« funksion ndihmĂ«s is_any_of:


enum state_type { IDLE, CONNECTED, DISCONNECTED };

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

Ky rezultat ndërmjetës shkakton zhgënjim. Deri tani kodi nuk bëhet më i lexueshëm:

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

Pak përmirësim në situatë do të ndihmonin parametrat e shabllonëve non-type. Me ndihmën e tyre do të transferojmë elementet e enum në listën e parametrave të shabllonit: 

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

Me përdorimin e auto në parametrin e shabllonit jo-type (C++17), qasja thjesht përfshihet në krahasime jo vetëm me elementët e state_type, por edhe me tipe primitive që mund të përdoren si parametra të shabllonëve non-type:


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

Me rrjedhën e tillë të vazhdueshme përmirësimesh arrihet sintaksa e dëshiruar për verifikime:


template 
struct any_of : private std::tuple { 
// le të trashëgojmë konstruktorët nga 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);

Në këtë shembull, udhëzuesi i deduksionit shërben për t'i treguar kompiluesit parametrat e dëshiruar të modelit të strukturës, duke njohur tipet e argumenteve të konstruktorit. 

MĂ« tutje – bĂ«het mĂ« interesante. Bjorn mĂ«son tĂ« generalizojĂ« kodin e marrĂ« pĂ«r operatorĂ«t e krahasimit pĂ«rveç ==, dhe mĂ« pas pĂ«r operacione tĂ« rastit. GjatĂ« procesit, shpjegohen karakteristika tĂ« tilla si atributi no_unique_address (C++20) dhe parametrat e modelit nĂ« funksionet lambdĂ« (C++20). (Po, tani sintaksa e lambdĂ«s Ă«shtĂ« akoma mĂ« e lehtĂ« pĂ«r t'u mbajtur mend – janĂ« katĂ«r çifte rreshtash tĂ« gjitha llojeve.) Zgjidhja pĂ«rfundimtare qĂ« pĂ«rdor funksionet si komponentĂ« tĂ« konstruktorit mĂ« ngroh shpirtin, pa pĂ«rmendur shprehjen tuple nĂ« traditat mĂ« tĂ« mira tĂ« llogaritjes lambdĂ«.

Në fund, mos harro të pastrosh!

  • Le tĂ« kujtojmĂ« se lambdat janĂ« constexpr falas; 
  • TĂ« shtojmĂ« perfect forwarding dhe ta shohim sintaksĂ«n e tij tĂ« shĂ«mtuar nĂ« lidhje me parameter pack nĂ« kapjen e lambdave;
  • TĂ« japim kompiluesit mĂ« shumĂ« mundĂ«si pĂ«r optimizime me conditional noexcept; 
  • TĂ« kujdesemi pĂ«r njĂ« raportim mĂ« tĂ« qartĂ« tĂ« gabimeve nĂ« modelet duke parĂ« vlerat e kthimit eksplicite tĂ« lambdave. Kjo do tĂ« detyrojĂ« kompiluesin tĂ« bĂ«jĂ« mĂ« shumĂ« kontrollime pĂ«rpara thirrjes sĂ« funksionit tĂ« modelit – nĂ« fazĂ«n e verifikimit tĂ« tipit. 

Për detaje shtoni referencat në materialet e ligjëratës: 

Përshtypjet tona

Pjesëmarrja jonë e parë në C++ Russia u mbajte mend për ngjeshjen e saj. U formua përshtypja për C++ Russia si një ngjarje e ngrohtë, ku kufiri mes mësimit dhe komunikimit të drejtpërdrejtë ishte thuajse i papërfillshëm. E gjithë gjendja, nga motivimi i folësve deri te garat nga partnerët e ngjarjes, nxit të diskutohen në mënyrë aktive. Pjesa informuese e konferencës, e përbërë nga ligjërata, mbulon një gamë të gjerë temash duke përfshirë risitë në C++, shembuj nga praktika e projekteve të mëdha dhe konsiderata arkitekturore ideologjike. Por do të ishte e padrejtë të injorosh në aspektin social të ngjarjes, që ndihmon në kapërcimin e barrierave gjuhësore jo vetëm në lidhje me C++.

Faliminderit organizatorëve të konferencës për mundësinë për të marrë pjesë në një ngjarje të tillë!
Postimi i organizatorëve mbi të kaluarën, të tashmen dhe të ardhmen e C++ Russia mund të keni parë në blogun JUG Ru.

Faleminderit për leximin, dhe shpresojmë që përmbledhja jonë e ngjarjeve ishte e dobishme!

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster