Názor ke článku Minimalistický http server v C++ od Ondřej Novák - > Takže typická předčasná optimalizace. Možná rychlejší řešení...

  • 2. 10. 2018 0:00

    Ondřej Novák

    > Takže typická předčasná optimalizace. Možná rychlejší řešení náchylná k chybám použítá místo standardních věcí.

    KDE je to náchylné k chybám? No a CO je na tom nestandardního? Hlavním důvodem této volby je ZEJMÉNA ušetřit si psaní No jen si to vemte. Kdybych zvolil mapu, znamená to beztak deklarovat seznam dvojic, což už tam mám. Dále bych musel deklarovat promenou typu std::map a v nějaké inicializaci do ní nakopírovat ten seznam. A nakonec vlastní vyhledávání bude naprosto stejné psaní, jen se podívej na to místo, kde se to používá. Místo std::lower_bound, tam bude mapa.find(). Ale zbytek bude naprosto stejný. Takže namísto abych se s tím takhle trápil jsem prostě vzal to pole, už seřazený a použil std::lower_bound, což je imho standardní stejně jako mapa. Navíc s bonusem toho, že to zabírá méně místa, a je rychlejší, protože nemusí dereferencovat pointery, ale používá půlení se stejnou složitostí.

    Seřadit 10 položek lze velice rychle pohledem. Seřadit 100 položek do zdrojáku lze snadno příkazem sort z terminálu. Pokud je to něco neměnné, statického, tak tohle je pro mě přímější cesta.

    >> constexpr

    Teď jste to vy, kdo předčasně optimalizuje. Pokud je mi známo, tak běžně překladač pozná, zda se u static const má zakládat proměnná nebo ne. Pokud vím, tak constexpr jen vyžaduje výpočet během kompilace. To rozšíření constexpr na static const a jestě k tomu inline je sice až v C++17 (což bych tedy pravda asi měl využít), ale obecně mám nedůvěru k věcem, kterým se často mění význam a tohle je jedna z těch věcí.

    >> šablona RAII

    Opět je to spíš důsledek lenosti psaní. Abych ty deskriptory měl plnohodnotné, musel bych všechny třídy deklarovat i se všema metodama a wrapovat je na posixové funkce. A to na to jsem línej. Takže se ty deskriptory používají prostě namísto číselných intů v posixových funkcí přímo. Nač bych se měl zabývat tím, že udělám plnohodnotnou třídu Socket se všemi funkcemi? Jen abych dosáhl nějaké 100% úrovně čistoty? Nehledě na to, že to moc dobře nejde, protože v unixovém světě je všechno deskriptor, takže bych stejně neuměl rozlišit soubor od soketu. Spoustu věcí je společných, spoustu věcí ne. Ale pokud máte víc času , tak si nad tím postavte třídní hierarchii.

    Jinak krásně ukazujete, že jste kód zas dobře nestudoval. Onen zbytečný stav "neinicializovaný" není zbytečný, protože jinak by vám nefungoval move constructor, který musí do toho objektu "odkud" se přesouvá něco dát, aby to se to pak destruktor nezkoušel zdestruovat. Navíc neplatný stav je též součástí posixových deskriptorů, takže, proč ho tam nepřiznat. Když zavoláte funkci socket() a ona vám vrátí -1, tak je to neplatný deskriptor a já snadno poznám, že je něco blbě.

    Jistě bych mohl zavolat
    auto s = std::make_uni­que<Socket>(AF_I­NET,....)
    a v konstruktoru vyhazovat výjimku když to selže.
    a vůbec si s tím pohrát.
    dokonce bych mohl udelat auto s = std::make_sha­red<Socket>(.­..) a měl bych ho sdílenej!
    ale výsledkem by byl delší kód, nevím jestli by byl o dost přehledný a byl by samozřejmě pomalejší a nenažranější. Ale možná bych se cítíl víc jako C++ guru ... možná.

    C++ jazyk je krásný právě tím, že nepředepisuje, jak se věci mají dělat. A prostě toto řešení mi přišlo rychleji naprogramované. A to zejména

    >> static

    Někdo vám nahoře napsal, že to právě ještě není vyřešeno, takže až to norma vyřeší, tak to začnu používat po novu. Zatím pořád anonymní namespace má external linkage, bohužel.

    >> operátor()

    Třída se jmenuje SplitString - je to málo popisné? Operátor () by se mohl jmenovat get_next().

    Kdo zná trošku můj kód, tak právě funktory často používám a též doporučuju všem. Protože mají velice univerzální použití. Pokud mám funkci, která čte nějaký data v nějakém formátu, nejlepší je, když ten vstup dostane jako T (šablona) a ona sama pak to T chápe jako funkci, kterou když zavolá, obdrží další položku ze vstupu. Takto to najdete v jakémkoliv kódu, který kdekoliv mám. SplitString je přesně dtto. Funguje jako funkce, která vrací další hodnotu ze vstupu.

    Je to trošku procedurální programování, trošku si hraní s lambdami, ale to mi přesně vyhovuje. Lambdy a funkce, které mají stav, miluju tuhle část normy.

    Ale jistě mohl bych napsat funkci split_string, která vrací vektor rozsekaného řetězce - a už jsme opět u toho, zase z toho je moloch, který alokuje někde nějaký buffer takže je to pomalé a komplikované

    Ad předčasná optimalizace? Ale nesmysl... to jsou osvědčené postupy, nad kterými prostě nepřemýšlím, nesedím u toho a neříkám, "tak jak to zoptimalizujeme". Je to ve stylu "tohle vždycky fungovalo nejlépe, tak to použijeme". Třeba použití std::string_view - jistě by každý všude měl std::string, ale - předčasná optimalizace, ušetříme alokaci tím, že budeme vytvářet pohledy na stringy - a já jsem rád, že se to do normy dostalo, protože to nepovažuju za předčasnou optimalizaci, ale za normální způsob programování. Tohle tam mělo být dávno.