Zobrazit příspěvky

Tato sekce Vám umožňuje zobrazit všechny příspěvky tohoto uživatele. Prosím uvědomte si, že můžete vidět příspěvky pouze z oblastí Vám přístupných.


Příspěvky - Ondřej Novák

Stran: 1 ... 16 17 [18] 19 20 ... 38
256
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 06. 02. 2014, 13:27:35 »

Co když to bude aplikace autopilota a ten destruktor vnořeného objektu bude nastavovat výškovku do defaultní polohy, aby letadlo letělo po ukončení autopilota rovně? Taky mě chyba ve vnořeném objektu nezajímá a v klidu ji můžu ignorovat, protože už vyletěla jiná výjimka? Sorry, ale na takovém předpokladu se žádná robustní aplikace postavit nedá.

Pořád zapomínáte, že to že letí výjimka neznamená, že se ten destruktor neprovede, to jen znamená, že pokud ten vnořený objekt skončil chybou, tak už není možnost to někam hlásit, když už jedna výjimka je v běhu. Pořád si neuvědomujete, že když letí vyjimka, program zrovna neprobíhá standardně, takže od něj nelze čekat žádnou funkčnosy. Funkcí destruktoru je uvést stav vsech datových struktur, zdrojů a podobně do původního stavu in best effort. Pokud to u aplikace takto náročné opravdu nejde, je třeba zvážit, zda není lepší ten program ukončit. To je ale věc, kterou si autor rozhoduje při psaní toho objektu. Záleží jak moc důležitý objekt to je.

To fatální ukončení aplikace pak může spusti nějaký nouzový režim.

Nikdy nelze chybu ošetřit s tím, že dopředu říkáme, že ošetření chyby také může způsobit chybu. A když i to se pokusím ošetřit, tak i tam mohu způsobit chybu. A tak donekonečna. Někdy se musím rozhodnout, zda to vzdám, nebo to udělám jak nejlépe to jde. I za cenu třeba ztráty dat, pokud je pro mne stabilita víc, než nějaká ztracená data (třeba to byly jen statistická data, nebo data, která lze později rekonstruovat). Důležité je, aby to byl tvůrce té konkrétní třídy, kdo rozhodne, zda na chybu reagovat výjimkou (i za cenu nebezpečí double exception), nebo výjimkou pokud to jde, nebo zaignorováním. Když hromadně zakážu výjimky v destruktorech, beru autorovi svobodu a tlačím ho pouze do toho zaignorování.

257
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 06. 02. 2014, 13:15:47 »

Co když to bude aplikace autopilota a ten destruktor vnořeného objektu bude nastavovat výškovku do defaultní polohy,
Tak si autor takové aplikace koleduje sedět za obecné ohrožení.

258
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 06. 02. 2014, 09:36:36 »
Reference counting je povolený, ale navenek nesmí být vidět. To je hodně těžké udělat. Navíc s přibývajícími jádry není reference counting žádná výhra a pro krátké stringy je daleko rychlejší když je to krátké pole přímo uvnitř stringu.
 že si z STL celkem konzistentně vybíráš věci, které se úplně nepovedly a ty povedené naopak ne.

No  hádám, že jsi člověk z nějaké akademické půdy, který nikdy nic pořadnýho nenaprogamoval, ale jen tu obhajuje svojí víru. Zkus za použití pouze stl psát nějaký větší projekt, který by měl být rychlejší, než kdejaký skriptovací jazyk, nebo jazyk s překladem do bytecode, abys obhájil smysluplnost programování v C++? Velice záhi zjistíš, že ta milenka standardní knihovna je pěkná potvora a ještě k tomu líná.

Pro tvojí informaci, problém s reference countingem u řetězců není v tom, že by to bylo pomalé v moderních procesorech... Ono to pomale je, ale stejně to pomalé bude i bez reference countingu, protože to je obecný problém sdílení dat mezi jádry a jejich synchronizace. Ten problém je mnohem hlubší, už v základním návrhu. Já časem pochopil, proč je snaha zakazovat ref counting. Je to snaha dodefinovat návrh tak, aby odpovídal původnímu záměru a netrpěl problémy, který tomu časem zařídili jednotliví implementátoři.

Reference counting nemá být skrytý. Naopak má být viditelný. Programátor musí dostat do rukou nástoje , jak s tím pracovat. Základní problém std::string je ten, že se má chovat jako vektor znaků dle definice. V tu chvíli nesmí být sdílený. Jakékoliv operace přidání znaku, změna znaku a podobně nutně vedou na vytvoření kopie, která interně získá jinou adresu (c_str() začne vracet něco jiného). naopak std::string nemá rozhraní pro možnost kopii řetězce a pochopitelně nic neříká o tom, co se děje s řetězcem v MT prostředí. Všechny tyhle nedotaženosti vedou k tomu, že se to implementátoři snaží řešit obecně a detekovat všechny ty výše popsané situace, tak, aby se to chovalo jako pole znaků.

Pokud naopak řeknu, že řetězec je od počátku otevřeně sdílený s reference countingem a zakážu přímý přístup k řetězci (jakmile jednou LightSpeed::String vznikne, je const a nedá se jeho obsah změnit, lze jedině vytvořit nový řetězec s upraveným obsahem), definuju jak se řetězec chová v MT prostředí (ref counting ve výchozím stavu není  MT safe, takže se ty řetězce nesmí sdílet mezi vlákny, to je záměr) a dám k dispozici rozhraní pro možnost odstranit sdilení (metoda isolate() ) nebo vyrobení fyzické kopie (metoda copy() ), tak je to úplně něco jiného, co člověku přinese spoustu výhod a zabrání spoustu chybám a neočekávaným komplikacím, za předpokladu, že dodrží všechna pravidla s tím svázaná.

Já v té knihovně nemám jediný objekt na řetězce. Mám tam ConstStringT<T> - odkaz na libovolnou sekvenci znaků kdesi v paměti, StringPool<T> - kolekce řetězců vhodná pro řetězcové mapy, StringParam<T> - řetězec nebo reference podle použití, hodí se při vyhledávání v mapách (není třeba alokovat paměť, pokud chci najít určitý řetězec v mapě), samozřejmě AutoArray<T....> - což je stejné jako std::vector<T> nicméně tenhle objekt se dá převést na ConstStringT<T>. Z toho jsou odvozené objekty StaticString<T, n> a SmallString<T,n>, kde n udává maximální počet znaků. Alokuje se to pak přímo v tom objektu. Rozdíl mezi StaticString a SmallString je ten, že ten první má to n jako pevný limit, a při překročení háže výjimku. Ten druhý, SmallString, to číslo se chápe jako hint a při překročení to způsobí jen snížení výkonu, protože se musí alokovat na haldě.

Nic takového mi stl nenabízí. Jenom string

KRYTON: Aha. Přiblížit hlavu těsně... ehm, k předmětu. Dobře, chápu. A co další optické jevy: obrazová montáž, zpomalení, kvantel...
LISTER: Nic takovýho nemáme.
KRYTON: (Skepticky) Nemáte to, jenom zoom. (Komicky se naklání k seznamu) No, nic, to je dobré, to úplně stačí, opravdu výborné

259
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 06. 02. 2014, 01:52:26 »

Opravdu? Potom by asi bylo dobré autorům STL vysvětlit, že to dělají úplně špatně a nemají reference counting používat:


Stav je takový, že tvůrci normy C++11 se docela lekly problémů, která by náhlá změna způsobila, že nakonec uvažovaný koncept do normy nezanesly. Nicméně stále by mělo platit, že jednoho dne bude prohlášeno, že reference counting u std::string nesmí být.

Já to mám jednodušší, já si udělal vlastní stringy a u nich jsem řekl, že reference counting je u nich naopak povinný. Vyhnul jsem se tak jakési "právní" nejistotě okolo std::string  (sorry, ale nemohu stavět program na něčem, o čemž předem vím, že se možná v budoucnu výrazně změní)

260
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 06. 02. 2014, 01:47:37 »
    • Je špatný návrh destruovat každý objekt hned poté, co jej přesunete (zkopírujete). Daleko lepší je přesunout (zkopírovat) všechny objekty a až poté destruovat, dost si usnadníte zachovávání konzistence při výjimkách. Navíc vyhazovat mohou nejen destruktory, ale i konstruktory, takže výjimky v tomto případě musíte řešit stejně.

    Tohle se obtížně řeší, když mám na přesun šablonovou funkci, takže nemohu vědět, co se děje uvnitř. To už asi nebude jednoduchá funkce, ale specializovaný objekt s dvěma metodama. Jedna pro přesun a druhá pro následnou destrukci. Já to řešil jednodušší cestou, protože mi přišlo zbytečně složité si komplikovat situaci, která nastane jen někdy (zpravidla nenastane). Ať už výjimka vypadne při kopírování nebo při destrukci, řeší se to rollbackem.

    261
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 22:55:29 »
    A co takhle to při výjimce hnedka všechno zrušit? Stejně se nedá spolehnout na to, co zůstane po tom přenosu zpátky, tak proč se vůbec obtěžovat?
    To myslíš vážně, nebo si děláš srandu?

    Citace
    A tos neviděl tu alchymii při vkládání prvků na pozici (posun všeho doprava), nebo při mazání jednoho až n prvků z prostřed. Tam si užiješ destruktorů... Já vím, že STL tohle neřeši. Je to jednoduché, stačí výjimky zakázat a tím je problém vyřešen. No není. Možná tak úředně.
    No právě protože vím jak těžké je zaručit exception-safety, tak jsem se na to ptal. Ono stačí zakázat výjimky v destruktorech a najednou jsou z neřešitelných problémů problémy obtížné. 8)
    Ale jde to zaručit, jen to nikdo nechce řešit. Při mazání se výjimka destruktoru řeší tak, že se objekt považuje beztak za zničení, takže rollback se řeší pokračováním operace ničení (a následného přesunu ovšem už v režimu rollback jako stack unwind, takže každý destruktor ví, že už letí výjimka). Totéž pokud dojde k výjimce při konstrukci během vkládání. Opět se to rollbackuje (destruuje již vložené a přesouvá zpět) a to v rámci stack unwindingu.

    A pokud by tě zajímalo, jak spustím operaci tak, aby se tvářila jako stack unwind, tak věř mi, je to jednodušší než si myslíš. Napíšu operaci jako třídu, tělo nechám vykonat v destruktoru. Pak jí zkonstruuj a ihned po její konstrukci udělám throw; Destuktor - potažmo vlastní rollback operace se provádí v rámci stack unwind.

    Takhle třeba řeším výjimku v destruktoru při ničení celého pole. Destruuje je odzadu a když dojde výjimce, pokračuju v destrukci dopředu ovšem jako stack unwind. Naprosto totožné se totiž děje, když ti při destrukci objektu jeden z memberů vyhodí výjimku.

    Sakra, všechno je v tom jazyce popsané. Celý systém je geniálně vymyšlený. Někdy mi přijde, že syntaxi a pravidla dělal jeden tým, zatímco ti méně schopní dostali na starost standardní knihovnu. Podle toho to tak vypadá. Jazyk má geniální logický pravidla, které ta knihovna ani neumí pořádně využít.

    262
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 22:34:29 »
    A co tedy navrhuješ?
    No já navrhuju neházející destruktory samozřejmě. Pokud můžou destruktory házet tak prostě řešení neznám a bojím se, že ho nevymyslel ani nikdo z autorů normy C++.

    Problém je už přenos jednoho prvku kombinací konstruktor-destruktor, pokud oba můžou selhat. Příklad :
    kopie zdroj->cíl OK
    destrukce zdroje - CHYBA - rollback
    kopie cíl->zdroj - CHYBA
    A co teď? S tímhle by si poradil leda destruktor, který při výjimce nic nelikviduje, ale ten zase nemusí zlikvidovat cíl, pokud by ta kopie zpátky prošla.
    Jestli sis ráčil všimnout, celé je to postavené na tom, že operace moveObject se dá specializovat. Takže tam vůbec nemusí být konstrukce->destrukce. Ale to je jedno. Klidně si tam může specializovat vlastní operaci, která výjimku z destruktoru zaignoruje a pojede se vesele dál. Ale ten princip zodpovědnosti za konzistenci se tím naruší a to není možné dopustit. To že se potom může ztratit konzistence rollbackem už není takový problém, jako když se ztratí konzistence při normální operaci (a nezahlásí se to jako výjimka). Že se ti někde při roztahování pole poztrácela data, to ti asi nevadí. Zato ti vadí, když se postrácej při rollbacku. No mě zase vadí spíš to první a při rollbacku je mi to v celku jedno, protože to beztak možná skončí destrukcí celého pole až někam na základní úroveň.

    A tos neviděl tu alchymii při vkládání prvků na pozici (posun všeho doprava), nebo při mazání jednoho až n prvků z prostřed. Tam si užiješ destruktorů... Já vím, že STL tohle neřeši. Je to jednoduché, stačí výjimky zakázat a tím je problém vyřešen. No není. Možná tak úředně.

    263
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 22:10:27 »

    Takže pokud při tom vyletí výjimka, tak vím že jeden objekt nepřežil cestu tam a kdo-ví-kolik jich zařvalo při tom rollbacku. Takže vlastně o obsahu toho vektoru nevím vůbec nic. No hlavně že je to rychlé. ;D

    A co tedy navrhuješ?

    264
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 21:52:29 »
    A tak všichni používají třeba string i tam, kde by stačilo třeba const char *. Jenže to není podle mantry, takže je to evil. Hnusi se mi tenhle masový styl.
    Pointer na char taky není nějaká hitparáda. Na rozdíl od stringu v sobě nemá délku, takže je ho třeba opakovaně procházet znak po znaku nebo tu délku předávat nezávisle. To není úplně šikovné. Navíc pokud si ho chci uložit, tak v případě pointeru musím alokovat a kopírovat. V případě stringu to je jeden inkrement(libstdc++, nevím jak jinde). Ten masový styl občas překvapí.

    FYO reference counting u STL řetězců je pokud vím zakázaný, nebo aspoň silně nedoporučovaný.

    místo const char * mám ConstStrA a ConstStrW. Fungují přesně tak jak píšete. Mají pointer a délku. Dál ty objekty umí třeba metody find, nebo substr. Drtivá většina funkci přijímají jako řetězec tento typ. Můj standardní string má samozřejmě konverzi na ConstStrX, a pochopitelně mám konverzi i na std::string. Hlavní výhodou je, že se s tím pracuje naprosto stejně jako s řetězcem, jen se to nikdy nikam nekopíruje, dokud není vyložene potřeba (třeba k uchování řetězce pro asynchroní operace).

    265
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 21:45:47 »
    Škoda že nejspíš nikdy neuvidím zdrojáky té LightSpeed knihovny. Zajímalo by mě, jak je tam řešená realokace vektoru (nebo toho podobného), když můžou destruktory házet. Už když destruktory házet nemůžou to není úplně triviální věc.

    To je easy.
    Realokace se řeší stejně jako v STL, rezervuje se vždycky nějaký kus paměti navíc, kam se pole rožšiřuje. Když už není kam vkládat, alokuje se kus prázdné paměti a pak se všechny prvky přesunou do nového místa. K tomu se používá operace LightSpeed::moveObject, který lze pro každý typ objektu předefinovat. Výchozí implementace je pro C++11 použití move konstruktoru, pro starší verze jako série konstrukce objektu na novém mistě copy constructorem a pak zavolání destruktoru na starém místě.

    Pokud dojde při operace move k výjimce ... může dojít i v případě, že se objekt přenáší nějak speciálně. Považuje se objekt, který se přesouval za nepřesunutý. Pokud je přenos realizován jako constructor-destructor, pak je tam ve výjimce v destructoru rollback jako zpětný přenos na původní místo. Jakmile dojde k výjimce při přesunu, všechny prvky doposud přesunuté se musí přesunout zpět na původní místo. Následně se dealokuje paměť a vyjímka se vyhodí ven.

    Pokud dojde k výjimce při všech rollback operací, tak je to double exception fault. Během rollback operací vrací funkce uncaught_exception true, takže každý další destruktor ví, že v tu chvíli by už neměl házet další výjimky.

    266
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 15:49:01 »
    Aj, std::shared_ptr, zachraň se kdo můžeš   ;D

    Kvůli pitomosti volat pomalej new  ;D

    Režie fopen je mnohem větší než režie new. Takže znovu: premature optimization is the root of all evil

    Ale to je pitomost. Proč mám sakra zbytečně zdržovat svůj program když na to už mám napsaný nástroj, který nic alokovat nepotřebuje? Proč? Jenom proto, že režije fopenu je mnohem větší, tak mi musí vadit těch pár mikrosekund (což mohou být i milisekundy, když se new umlátí na zámku s nějakým jiným vláknem). Jenom proto, že někdo slavný prohlásil takovou pitomost? Je jasné, že snažit se optimalizace tam kde to nemá cenu je  blbost, ale pokud je to pro mne jen změna knihovny a jména objektu, tak to smysl má.

    Problém totiž je vtom, že tyhle mantry opakují všichni, aniž by se nad tím zamysleli. Tady to asi nemá velký význam, ale pak vidím kód, kde se to zneužívá až tak, že jediné co program dělá je, že tráví většinu času v knihovnách STL. Jen proto, že někdo kdosi kdysi řekl, že premature optimization is the root of all evil.

    A tak všichni používají třeba string i tam, kde by stačilo třeba const char *. Jenže to není podle mantry, takže je to evil. Hnusi se mi tenhle masový styl.

    267
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 15:37:04 »
    Samozřejmě, námaha by měla odpovídat efektu. Nicméně stdio není ekvivalentní streamům, neumí RAII. A když k tomu napíšete RAII obal, velmi rychle zjistíte, že vlastně začínáte psát vlastní streamy :)
    std::shared_ptr<FILE> f(fopen(...), fclose); je vlastní stream?

    Aj, std::shared_ptr, zachraň se kdo můžeš   ;D

    Kvůli pitomosti volat pomalej new  ;D

    268
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 15:33:09 »
    Pan Novák samozřejmě udělal 1. Když už to dělal asi po třetí, napadlo ho, že by si na to mohl udělat přípravek.

    Proč jste to psal znovu a nepoužil třeba tohle?
    http://fastformat.sourceforge.net/

    Protože jsem potřeboval binární streamy. Vůbec nakonec moje streamy žádné formátování nemají. Formátování řeší objekt TextOut, který dostane jako parametr binární stream. Asi jsem trochu nakažený javou.

    269
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 15:15:15 »

    Pan Novák samozřejmě udělal 1. Když už to dělal asi po třetí, napadlo ho, že by si na to mohl udělat přípravek. Z přípravku vznikla nová knihovna pro rychlejší streamy. A protože byly nejen rychlé, ale časem si na ně pan Novák zvyknul, přizpůsobil zbytek programu těmto novým streamům a začal je používat všude. Už proto, že na ně převedl veškerou komunikaci po síti nebo třeba roury a podobně. Dokonce má i http stream.

    Nicméně ještě před bodem 1 bych přidal jeden marný pokus napsat vlastní implementaci iostreamu ... ale dědit něco z STL se prostě nedá.

    270
    Vývoj / Re:C++ a výjimka v destruktoru
    « kdy: 05. 02. 2014, 15:07:02 »
    ....

    Zapoměl jsem dodat... za předpokladu, že ten flush selže... Protože jinak to funguje jak má. Proto slovo "může".

    Stran: 1 ... 16 17 [18] 19 20 ... 38