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 ... 17 18 [19] 20 21 ... 38
271
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 15:06:01 »
Díky tomu prostě nemůžu ve svém kódu spoléhat na to, že když nevyletěla výjimka, tak mi v tom destruktoru flush prošel. Můžu leda tak v dokumentaci zakázat, aby můj kód někdo volal z destruktoru.

Samozřejmě že:
Kód: [Vybrat]
U::~U {
      BufferedFile f("ahoj.txt");
      f << "nazdar";
}

může při výjimce způsobit prázdný soubor. Vyřeším to tím, že tam strčím explicitně f.flush(). Ve vašem případě by byl povinný. Ale je otázkou, jestli tohle vůbec je vhodné místo v destruktoru dělat. Jestli to vůbec svědčí o správném návrhu.

272
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 14:41:11 »
Napíšete do dokumentace: "Vždycky na konci bloku zavolat JHS.close(), jinak vám to nebude fungovat".
Vaše řešení zase o tolik lepší není. "Destruktor se pokusí flushnout buffer. Pokud se mu to nepodaří, tak možná vyhodí výjimku."
Nezapomínejte, že váš kód se může volat z místa kde není úplně jasné, jestli se to právě vykonává při úklidu, nebo při normálním průběhu.
Citace
Nebo mi napište, jak byste to řešil vaším explicitním zavíráním, kdy nemůžete v destruktoru vyhodit výjimku. Co když tedy ten flush selže? Co dál?
A co takhle si na nic nehrát a do dokumentace to napsat? "Destruktor se pokusí flushnout buffer, ale může to selhat. Pokud je to důležité, flushněte ho ručně předem."

Není to to samé. Já nevkládám povinnost na bedra programátora. Destruktor se snaží vyřešit výjimečnou situací in best-effort. Což je lepší, než se na to vykašlat a spoléhat na programátora, že to udělá sám. Ani mé řešení nevylučuje si ten flush zavolat ručně, pokud je to důležité. Cílem však je, aby tam, kde to není potřeba se to neřešilo.

(ona ten váš explicitní flush totiž vyžaduje, aby se to řešilo i v situaci, kdy k žádné chybě nedošlo, zatímco řešení destruktorem to vyžaduje jen v situacích, kdy takové nebezpečí hrozí. Za mé praxe jsem se setkal jen s jedním případem, kdy se uncaught_exception nedalo použit, protože zrovna byla true. Ale to byl speciální případ a navíc nakonec i špatný návrh). Pořád si myslím, že mé řešení je pro programátora mnohem příjemnější i za cenu toho, že ve velmi speciální a zpravidla chybových stavech se prostě ty data neflushnou.

273
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 14:15:25 »
a jak to dělají, když v destruktoru vyhodit výjimku nesmějí. Jak jinak to dělají. Jediný co můžete s takovou výjimkou udělat je, že jí vyhodíte, nebo zaignorujete.
A co takhle dát to jinam, než do destruktoru. Pokud tu výjimku nemůžu ignorovat, tak to nesmí být v destruktoru, protože tam se o ní občas nedovím ať dělám, co dělám.
Když to začnete řešit takhle, narušujete koncept RAII. Protože vyžadujete explicitní uzavření prostředku, což je proti duchu RAII. Co když na to zapomenete?

Citace
A jak to uvnitř objektu poznáte, že na tom nesejde? A co když na tom sejde?
To samozřejmě poznám už ve chvíli, kdy ten kód píšu. Pokud na tom sejde, tak si nemůžu dovolit ani
A co když píšete knihovní objekt? Kde to jestli na tom sejde nebo nesejde záleží na tom, kdo to bude používat? A co když přitom nechcete narušit koncept RAII? Napíšete do dokumentace: "Vždycky na konci bloku zavolat JHS.close(), jinak vám to nebude fungovat". Už vidím tuny kódu, kde na to lidi zapomenou.
[/quote]
Zalogování chyby je pro vývojáře a ne pro uživatele.
Co třeba logovat chyby pro helpdesk?

Citace
Výjimky se nesmí používat k normálnímu běhu kódu.
To jsem ani nikde nepsal. Já psal o případu, že se někdo jako vy pokusí v destruktoru uložit při chybě soubor pomocí vaší knihovny. V téhle chvíli se vaše knihovna s vaším bufferem bude tvářit jako že je všechno ok, i když uloží neúplný soubor.
A to jenom pokud to selže výjimkou. Ten způsob řeší opravdu jen situaci, kdy něco selže dvakrát za sebou. Nebo mi napište, jak byste to řešil vaším explicitním zavíráním, kdy nemůžete v destruktoru vyhodit výjimku. Co když tedy ten flush selže? Co dál?

Citace
...nebo když používam custom alokátory (které STL neumí - i když vám kdejaký jouda bude tvrdit, že ano).
Neumí to STL, nebo Novák?  :P
To je právě to, STL custom alokátory jsou velmi nepraktické a nepoužitelné. Bohužel jsou součástí  standardu.

Aha, takže až vydáte tu Vaši knihovnu, tak celý business zahodí STL a začně to používat, protože všichni pojídači koláčů, kteří se někde na akademické půdě naučili STL, budou mít konečně mnohem rychlejší a použitelnějsí standardní knihovnu do C++ :).

troll alert

274
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 14:01:09 »
Premature optimization is the root of all evil:
http://c2.com/cgi/wiki?PrematureOptimization
Moje zkušenost je taková, že pokud bylo něco příliš pomalé, tak to bylo chybně zvoleným algoritmem a bylo to stejně nutné celé předělat. K apriori optimalizacím jako vrazit tam pro jistotu jiný alokátor nebo custom stream jsem skeptický.

Akademické půdě je jedno, jestli algoritmus trvá 10 sekund nebo 100 sekund, důležité je, že má složitost N a ne N^2. Zákazníkovi to zpravidla ale vadí, a pokud dokážete svůj kód zrychlit desetkrát... budou vám líbat ruce.

... a to jenom proto, že jste místo std::string použili můj LightSpeed::ConstStrA.

Na akademické půdě lze připustit, že v praktickém nasazené lze kód optimalizovat. V bysnysu ale na to zpravidla nejsou čas ani peníze.

Navíc problém STL je ten, že Vás (a Váš kód) zdrže svými svazujícími pravidly (například nemožnost strkat do kontejnerů objekty bez povolené operace přiřazení, nebo s výjimkami v destruktorech) jenom proto, aby to bylo spíš akademické, než praktické. Takže možná tak na výuku programování. V bysnysu... ne. Ono to končí zpravidla tím, že programátor začne stl obcházet a hackovat (viděno na vlastní oči, ač si to většinou autoři neuvědomují... považují to za součást stylu)

275
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 13:34:40 »
void on_double_exception()  throw();
Proc to zastavovat u druhe vyjimky? Co kdyz nastane k-ta vyjimka? Zavedme si funkci on_kth_exception(int k)!
Ve kterém bodě by nastala k-ta výjimka. Podívejte se na tu definici znova. Vidíte throw()?

Definice měla být takováto. Pokud při zavolání destruktoru během stack unwind dojde k výjimce, zavolá se funkce on_double_exception s tím, že current exception bude ta, která byla vyhozena. Funkce on_double_exception() bude považován za handler, takže throw; způsobí nové výhození aktuální výjimky. Pokud funkce skončí, bude výjimka považovaná za vyřešenou a program se vrátí k původnímu stack unwind. Pokud však funkce skončí s výjimkou, pak už asi nezbývá jiné řešení, než terminate. Protože tahle funkce by měla řešit double exception. Kde by nastala třetí exception? Pokud v té funkci, tak si jí musí ošetřit sama. Pokud při stack unwindingu v rámci té funkce, tak se to musí pořešit rekuzivním voláním téže funkce. Klidně to může jít do nekonečna, až dojde zásobník (v krajním případě)

Opravdu to stojí za to? Přináší vyhazování výjimek v destruktoru, psaní custom STL nekompatibilních alokátorů, vlastních streamů a obecně nepoužívání STL takové benefity, že se to vyplatí? Budou takový kód ostatní programátoři rádi studovat a udržovat? Bude to stejně prověřené a odladěné jak STL, které používají miliony uživatelů?

Samozřejmě přináší. Zpravidla výkon. Nezanedbatelné zrychlení. Dělal jsemi nějaké benchmarky. Používá se to stejně snadno jako STL, tedy pokud si to někdo nastuduje s přiložené dokumentace. Chystám se knihovnu udělat open-source, ale to vyžaduje práci navíc (vyházení obsolete věcí a revizi dokumentace) a momentálně na to nemám čas.

276
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 13:10:40 »
Spíš ignorační, ne? Už podle deklarace je jediným účelem téhle funkce se té nechtěné výjimky prostě nějak zbavit.

Takový návrh prostě narazí u té části lidí, kteří vyjímky chytají a neignorují.
a jak to dělají, když v destruktoru vyhodit výjimku nesmějí. Jak jinak to dělají. Jediný co můžete s takovou výjimkou udělat je, že jí vyhodíte, nebo zaignorujete.

No a říct o lidech jako Stroustrup, Alexandrescu a podobných, že jsou to úředníci co neradi myslí ...  ::)

Jsou to lidé. Ne bohové. Alexandrescu má spoustu nápadů a některé práce jsem od něj četl a považoval jsem je za skvělé. Jindy mi to přišlo jako blbost a úchylárna. Třeba například jeho alokátor malých objektů byl bezva. Nicméně časem jsem ho vylepšil a začal používat jinak, protože pro jeho globalní řešení jsem nenašel uplatnění.

 Stroustrup.... no prostě to je člověk, který programuje úplně jinak než já. Co jsem od něj četl, to byla většinou pěkná prasárna. Tak jsem ho začal ignorovat. Tím ho nechci schazovat, jen mi úplně jeho styl nesedl


A ta vnořená destrukce házet může, nebo ne? Vždyť likviduje úplně to samé, jako ta vnější. Nebo se to, co se nepodaří zrušit, taky prostě odignoruje?
Napadáte příměr. Říkal jsem, že to lze chápat jako uvedení objektu do původního stavu a jeho následující destrukci. Nebo to berte tak, že to je uvedení objektu do stavu, kdy ho lze bezpečně a bez výjimek zdesrtruovat.

Když na tom nesejde, tak ano. Ale když na tom nesejde, tak můžu chyby odignorovat rovnou a nemusím nic házet.
A jak to uvnitř objektu poznáte, že na tom nesejde? A co když na tom sejde? Samozřejmě, že ve wraperu kolem FILE mám mojí oblíbenou konstrukci se std::uncaught_exception(). Nehledě na to, že ten wrapperu už stejně delší dobu nepoužívám.


Takže neuložení souboru je špatně, ale když neprojde flushnutí bufferu a díky tomu ten soubor nepůjde otevřít, tak je to něco jiného? Já v tom žádný principielní rozdíl nevidím. V obou případech je to nespolehlivé.

Záleží, jaké máte zadání. Jak se to bude používat. Pokud píšu objekt řešící streamování, tak nejspíš chci ukrýt nějaké vnitřní bufferování, které má za úkol, že mi optimalizuje zápis. V normální použití mi přijde naprosto nepřijatelné, abych se staral o flushování dat. Někde na to zapomenu a nastane problém. Prostě se streamem se pracuje tak, že buffer je vnitřní záležitost, která se nepřenáší ven. A pokud stream má funkcí flush(), tak se to většinou chápe jako hint (funkce může být prázdná).

U objektu Word který obsahuje dokument je věc odlišná. Dokument je věc veřejná o které se navenek ví. Je dokonce zadané, že ne vždy je uložení objektu chtěné - zatímco u streamu se předpokládá, že nabufferovaná data se vždy uloží,  u dokomentu ve wordu to není automatický předpoklad.

A mohl bych pokračovat dál, třeba u transakce se nepředpokládá, že by se automaticky commitovala. To nikdo zpravidla nechce, protože od toho transakce jsou. Člověk je chápe jako sandbox, do kterého si připraví všechny operace a až když to má, tak to commitne. Objekt Word lze stejně podobně chápat. Je to spíš sandbox, než stream.

Dobrý programátor si umí udělat analýzu problému a hned vidí, co je a co není logické a jak by to měl používat. Špatný programátor klade takovéhle otázky, aniž by se nad tím zamyslel.

Jak už jsem psal. Pokud šoupnu flush do destruktoru a budu na to spoléhat, tak program sem tam vyprodukuje binec. Pokud budu házet, tak některé případy chytnu ale některé stejně projdou. Pokud mi na tom nefičí, tak nemusím házet vůbec. Pokud mi na tom záleží, tak mi házení moc nepomůže.

Jestli vám některé případy stejně projdou, tak byste se měl hluboce zamyslet nad tím, k čemu používáte výjimky. Možná je nepoužíváte správně. Výjimky řeší výjimečné situace, zpravidla situace, kdy se operace nezdaří. Program se většinou píše tak, že se předpokládá, že všechny operace se zdaří.  A pokud je chyba správnou operací, pak se neřeší výjimkou. Takže špatný předpoklad. Ten flush se provede vždy. Když se provádí uvnitř výjimky a nezdaří se, tak se tedy zaignoruje, protože je vysoká pravděpodobnost, že jde o zavlečenou chybu, tedy že to nezdaření je stejně důsledkem již probíhající výjimky. Vaše "někdy stejně projdou" je akademická diskuze a spíš ukazuje na hluboké nepochopení problematiky.

Představte si, že je ten buffer použitý někde hluboko uvnitř knihovny. Už vidím tu dokumentaci: Pozor, během stack unwindingu někdy nedojde k zápisu celého souboru! :o

nic takového tam samozřejmě nebude. Viz výše. Výjimky se nesmí používat k normálnímu běhu kódu.


Házející rollback? V jakém stavu je ta transakce, pokud rollback vyhodí chybu? Co v takovém případě vlastně můžu dělat? Na tomhle příkladě smrdí daleko víc, než jen házení z destruktoru.

Představte si kod:
Kód: [Vybrat]
{
  {
   Transaction trn1(connection);
   trn1.neco();
   }
  {
   Transaction trn2(connection);
   trn2.neco();
   }
}
Pokud vylítne výjimka v destruktoru první transakce, tedy rollback vyhodil výjimku, něco nepěkného se nejspíš stalo se spojením s databází. Pak je dobré ukončit tento blok, než se snažit pokračovat vytvářením druhé transakce. Navíc chybová hláška z první výjimky bude asi znít "connection lost", zatímco druhá výjimka, která velmi pravděpodobně vypadne při vytvoření druhé transakce už může znít "invalid socket". Vyhození výjimky při rollbacku je tedy lepší a bezpečnější řešení.

Nikdy jste neprogramoval databáze? Že se divíte rollbacku házící výjimku.


Někoho kdo ignoruje to, že se STL musí vypořádat se spoustou protichůdných požadavků a díky tomu vypadá tak, jak vypadá? Nebojte, za génia Vás určitě považovat nebudu.
Já jsem neříkal, že jí ignoruju, já jsem říkal, že jí nepoužívám. Jak bych mohl, když mé objekty háží v destruktoru výjimky, nebo když používam custom alokátory (které STL neumí - i když vám kdejaký jouda bude tvrdit, že ano). Používám vlastní streamy (zkuste někdy napsat vlastní verzi std::iostream.... dlouhe zimní večery už jsou minulostí). Takže neignoruju, jen nepoužívám. V mých knihovnách není problém STL používat, ale zpravidla se ukáže, že je to k ničemu.

277
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 12:38:14 »
udržovatelné konstrukce. Znát jazyk jako svoje boty neznamená "můžu použít každou prasečinu, která v tom jde udělat".

To s tím nesouvisí. Standardní knihovny jsou plné prasečin. Jen si někdy projdi zdrojáky stl. Najdeš někoho, kdo by jim rozuměl? To je plné prasáckých konstrukcí!

 Základem úspěšného vedení týmu je dokumentace kódu. Není možné vést tým tak, že ho budu nutit psát tak, aby to bylo čitelné bez dokumentace. Dokumentace je základ. Minimálním nárokem je dokumentace rozhraní, kdy dokumentace plní také roli podrobné specifikace (pak je snadné rozhodnout, jestli odlišné chování programu je chyba nebo to tak autor skutečně zamýšlel).

278
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 11:05:40 »
Vyhazovat výjimky v destruktoru je zakázané snad ve všecEverything they attempt shall succeed: Never allow an error to be reported from a destructor, a resource deallocation function (e.g., operator delete), or a swap function. Specifically, types whose destructors may throw an exception are flatly forbidden from use with the C++ standard library.

Pokud si někdo myslí, že je lepší než tvůrci všech možných coding standards, tak ať si výjimky v destruktorech vyhazuje, ale prosím neprezentovat to jako skvělou věc ostatním.
Považujte mě za génia (minimálně za člověka, který 20 let programuje z toho 15 let v C++). Ale tvrdím, že standardní C++ knihovna byla napsána dementy pro dementy. Spoustu věcí v ní nejde, takže už jí několik let (bude to za chvíli přes 10) nepoužívám , maximálně tak okrajově.

Zákaz výjimek v destruktorech považuju za vážné narušení konceptu RAII a je mi fuk, že to zakazuje tisíce a jeden coding standard, protože RAII je nad tím a jakékoliv jeho narušení znamená, že se na to nemohu spolehnout. Lidi co vymýšlely tyhle coding standardy samozřejmě častokrát RAII nemají v krvi, takže vymýšlí blbosti. Je to trošku jiné programování a denně se kolem přesvědčuju, že ho ovládá tak jeden člověk z deseti.

279
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 10:49:43 »
Já tuhle vlastnost C++ naopak docela chápu. Lidi z ISO nebyli schopni najít řešení, které by nějak konzistentně fungovalo. Takže místo toho aby z toho udělali nedefinovanou/nespecifikovanou operaci, tak řekli že je to porušení invariantů +- na úrovni assertu. A pokud chci házet z destruktorů, tak to musím překladači explicitně říct.

Jak řešit vyhození výjimky při unwindingu jiné? Potichu ignorovat původní, nebo novou výjimku? Nebo je nějak poskládat?
Co třeba rezoluční funkci? Když už nás někdo nutí implementovat si vlastní verzi terminate() tak taky mohli zavést rezoluční funkci na situaci, kdy vznikne double exception. Ona by možná stačila funkce, která by situaci řešila než volat terminate a tvářit se jako že programátor je blbec,

void on_double_exception()  throw();

Pokud by nastala dvojitá vyjímka, program by ihned skočil do této funkce, s tím, že by to bylo součástí nějakého univerzální catch handleru před tím, než by se pokračovalo v unwindingu původní výjimky

Kód: [Vybrat]
void my_on_double_exception() throw {
try {
   throw;
} catch (std::exception &e) {
   logException(e);
}
}

std::set_on_double_exception(&my_on_double_exception);

V C++11 existují lepší nástroje na vyzvednutí aktivní výjimky, tam by taková konstrukce nebyla potřeba

Myšlení bolí, a úředníci velice neradi myslí.



Vyhozená výjimka znamená, že se operace neprovedla a objekt by měl být v nějakém konzistentním stavu. Ideálně tak, jak byl před začátkem té chybné operace aby se dala třeba zopakovat. Akorát že v destruktoru je to naprosto nechtěné chování. Takže pro destruktor se musí napsat úplně znova všechno, co zavání rollbackem(v rámci toho rušeného objektu), protože ty operace místo návratu musí pokračovat v likvidování. To znamená, že ten házející destruktor musí pro případ, že by vyhodil výjimku obsahovat i nějaký kompletní úklid. Nepodobá se to tak trochu neházejícímu destruktoru?

Nevidím v tom problém. Výjimka vyhozena v destruktoru znamená, že objekt byl přesto zničený. Výjimka má za úkol přerušit další běh programu a nasměrovat ho k nejbližšímu exception handleru a předat mu chybový objekt. Nemusí to nutně znamenat, že se operace rollbackne. Neříká to nic o tom, že operace nebyla provedena. Je samozřejmě lepší, když funkce, která hodila výjimku uveden objekt do původního stavu, ale to je pouze SHOULD, nikoliv MUST. V případě výjimky destruktoru jde vlastně o uvedení objektu do původního stavu a jeho následnou destrukci, což vede k tomu, co jsem říkal a žádný logický rozpor tam není.

A co se dá dělat v případě, že destruktor vyhodí výjimku? Objekt už neexistuje, takže se z toho nedá nijak vzpamatovat. Musí se kompletně zopakovat celá operace, jejíž závěr (třeba zápis patičky nějakého souboru) takhle selhal. Pokud ten házející závěr vyhodím do vlastní metody, tak pokud vyhodí výjimku, tak ji můžu chytnout ještě před zlikvidováním objektu a možná to půjde nějak zachránit.
Jak? Co děláte, když vám ve standardním C funkce fclose() oznámí chybu? Zpravidla nic, zaignorujete to.

Citace
Upon successful completion 0 is returned. Otherwise, EOF is returned and the global variable errno is set to indicate the error. In either case any further access (including another call to R fclose ()) to the stream results in undefined behavior.

Házející destruktor sice zahlásí chybu, ale všechno zlikviduje jako by se nechumelilo. Něco jako by word při vypínání zahlásil, že se soubor nepodařilo uložit, ale dokument by stejně zavřel.

To je validní řešení. Destruktor je něco jako konečné řešení. V destruktoru by se neměla dělat validace požadavku. Není problém volání metody Word::save() před jeho destrukcí. Nehledě na to, že je dost diskutabilní, jestli by destruktor objektu Word měl volat save. Nicméně destruktor nějakého bufferovaného streamu by měl zavolat flush() (viz dále)

Máte nějaký příklad, kdy je házení z destruktorů rozumné řešení? Já jsem se bohužel s takovým ještě nesetkal. Ve všech případech, kdy jsem měl chuť házet z destruktoru, to bylo jen mojí leností a pokud jsem si líp promyslel chybové situace, tak mi ty házející kousky kódu vybublaly z destruktorů ven.

Kód: [Vybrat]
~WriteBuffer() {
try {
flush();
} catch (...) {
if (!std::uncaught_exception())
    throw;
}
}


Kód: [Vybrat]
Transaction::~Transaction() {
try {
rollback();
} catch (...) {
//catch exception if we are in exception
if (!std::uncaught_exception()) throw;
}
}
(ukázky z mého kódu)


280
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 08:29:16 »
Malá poznámka k tomu Rollback

Citace
//  Variant: Another wrong solution
    //
    Transaction::~Transaction() {
      if( uncaught_exception() ) {
        RollBack();
      }
    }

Já jsem si neuvědoml, že tam nemá vykřičník. Takže ten rollback vlastně volá jen když letí výjimka. No to už je totální blbost a za tenhle kód bych strhával prémie!

Zrovna u transakcí se používá explicitní commit a implicitní rollback v destruktoru. Každopádně platí oboje, ať už s vykřičníkem, nebo bez, oboje je špatné použití uncaught_exception.

281
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 05. 02. 2014, 08:20:25 »

Pokud z destruktoru vyletí výjimka během stack unwindingu, tak se nezahodí, ale volá se std::terminate. Což obvykle znamená, že se destruktor ukončí i se zbytkem programu. Pro terminate se dá nastavit vlastní handler, ale v něm se stejně moc zachránit nedá.

No tohle je přesně ta krásná vlastnost C++, kterou naprosto nechápu... respektivě chápu, že lidi z ISO nebyli schopni najít rozumné řešní, tak se k tomu chovají jako pštros, schovají hlavu do písku a řeší to tím nejhorším způsobem, jakým se to řešit dá.

Základní pravidla dobrého programátora: "Program by neměl spadnout". Nelze zákazníkovy vysvětlovat, že program spadnul jen proto, že mu selhala nějaká operace a zrovna zároveň mu spadla síť, takže došlo k dvou problémům naráz a program to psychicky neunesl a zhroutil se

http://www.gotw.ca/gotw/047.htm

Nesmíte věřit všemu, co se na internetu píše. Ten člověk má sice pravdu, že ta funkce uncaught_exception je hloupá a vrací true, když probíhá stack unwinding, ale už nevyřeší, zda výjimka vyhozena v tomto okamžiku způsobí problém.

Jenže už to neumí vyřešit a navíc ukazuje příklady chybného použití této funkce. Nejprve ale jedna malá analýza.

V případě, že nechci použít uncaught_exception, tak ja budu výjimku v destruktoru řešit? No asi nijak, tak jak to řeši většina běžných coderů
Kód: [Vybrat]
~Object() {
try {
  ....
} catch (...) {}
}

Když budu chtít být podctivější, napíšu to takto

Kód: [Vybrat]
~Object() {
try {
  ....
} catch (std::exception &e) {
   logException(e);
}
}


... tedy za předpokladu, že mi funkce logException nevyhodí výjimku. Benefit, který mi přináší použití std::uncaught_exception je ten, že mi říká naopak... "nyní je bezpečné výjimku vyhodit, takže se ten problém dostane k někomu povolanějšímu.". Samozřejmě, že není blbej nápad to zalogovat tak jako tak

Kód: [Vybrat]
~Object() {
try {
  ....
} catch (std::exception &e) {
   logException(e);
   if (!std::uncaught_exception()) throw;
}
}

To se mi tedy nejhůře může stát je, že se destruktor bude chcovat tak, jako by se choval v okamžiku, kdy bych ho deklaroval nothrow() a výjimku si pořešil uvnitř sám. Kde v tom vidíte problém?

Zpět k tomu odkazu. Ten člověk nakonec demonstruje, že nerozumí nástroji, který se mu dostal do ruky. Například,

Kód: [Vybrat]
   //  Variant: Another wrong solution
    //
    Transaction::~Transaction() {
      if( uncaught_exception() ) {
        RollBack();
      }
    }

Má pravdu, je to wrong solution a je to také ukázka nesprávné použití uncaught_exception(). On totiž nepoužívá tu funkci proto, aby se vyhnul dvojitý výjimce, kterou zrovna řeší, on to používá proto, že má obavu, že by Rollback() vyhodil výjimku a způsobil problém. A to je špatné použití uncaught_exception. Tak se to opravdu dělat nemá a ten kdo to použije si skutečně koleduje o malér. Správná řešení je:


Kód: [Vybrat]
    Transaction::~Transaction() {
      try {
        RollBack();
      } catch (...) {
        if (!std::uncaught_exception()) throw;
      }
    }


No je to pořád stejné, pořád to je jen o tom, že se destruktor snaží rozhodnout, zda je bezpečné hodit výjimku nebo ne. To co autor navrhuje v tom článku nakonec je opravdu z říše pohádek... použití zavíracích funkcí totálně zničíme koncept RAII. Pokud už bych v tom viděl výhodu, tak maximálně v situaci, kdy zavírám potencionálně nebezpečnou operaci v destruktoru, a chci mít jistotu, že výjimka propadne až ke mě. Ta možnost ručního uzavření by tam mohla samozřejmě být, stejně tak jako můj objekt pro transakci obsahuje i funkci rollback() a její implicitní volání řeší pouze případy, kdy je potřeba transakci rollbacknout během výjimky. Ale nelze to vzít jako konečné řešení a výjimky zakázat.

Kód: [Vybrat]
  //  The right solution
    //
    T::~T() /* throw() */ {
      // ... code that won't throw ...
    }

Pan autor má smysl pro humor. Kolikrát mu program spadnul před zákazníkem na SIGABORT?

Kód: [Vybrat]
 //  Alternative right solution
    //
    T::Close() {
      // ... code that could throw ...
    }

    T::~T() /* throw() */ {
      try {
        Close();
      } catch( ... ) {
      }
    }

Tady ukazuje, že pan autor vlastně neudělal nic. Vyjímku zaignoruje (ani ji nezaloguje) a program sice nespadne, ale rozhodně nemusí fungovat. Pokud by do větve catch přidal

Kód: [Vybrat]
        if (!std::uncaught_exception()) throw;

Rozhodně by svému kódu neublížil.

Opravdu to nejde udělat jinak?

Jde, ale musí se to umět.


[…] The deallocation function is called regardless of whether the destructor for the object or some element of the array throws an exception.

Dík, to jsem nevěděl, domníval jsem se, že u přetížených operátorů to bude problém... v "některých" překladačích.


Já používám
Kód: [Vybrat]
~Object()
try {

} catch (...) {
if (std::uncaught_exception())
return;
}

To jsem používal, ale mám pocit, že s tím má někdo problém... Mám pocit, že ve Visual Studiu se to nechovalo úplně správně. A zápis občas rozhodil nějaká IDE (myslím že eclipse).


282
Vývoj / Re:C++ a výjimka v destruktoru
« kdy: 04. 02. 2014, 22:05:30 »
Výjimka v destruktoru je výborná věc, pozor jenom na to, že masa podprůměrných céčkařů tady bude křičet a dupat nožičkama, že je to evil evil evil asi jako goto v Céčku.

Výjimka v destruktoru se provádí tak, jaky by v destruktoru byl return. Tedy destruktor se považuje za vykonaný, i když skončil výjimkou. Jestli se provádí nějaký stack unwind, tak maximálně v rámci destruktoru. Následně se děje to, že bez ohledu na to, jak skončil destruktor, program normálně pokračuje v destrukci objektu, jako by se nechumelilo až do okamžiku, kdy je objekt úplně zničený. Pak ale nastane zásadní změna. Bez výjimky by program pokračoval dalším příkazem následujícím po destrukci objektu. Pokud však destruktor vyhodil výjimku, pokračuje se hledáním prvního exception handleru, který by mohl výjimku odchytit, přičemž se zahájí stack unwind jako obvykle.

Takový drobný problém. delete obj

Chce to vyzkoušet, ale logika říká, že pokud destrukce objektu skončí výjimkou, paměť se neuvolní. Je samozřejmě možné že ano, ale chce to vyzkoušet i v případě, že delete byl přetížený (tam by se pak volal operátor, který se ale při výjimce nemá co volat). Odlišná situace je, pokud by obj měl virtuální destruktor, tam se myslím delete zavola ( jako poslední akce destrukce objektu). Pro jistotu je lepší používat chytrý ukazatel.

Výjimka v destrukoru má neocenitelnou funkci přerušení programu a podání zprávy v okamžiku, kdy součastí destrukce je operace, která selhala a defacto vylučuje další běh programu... je to prostě výjimka. Příklad... flush nějakého bufferu.

Proč je to tak kritické téma?

Jednou z věcí je, že hodně programátorů si neuvědomuje, co to všechno znamená. Jak moc musí změnit své programátorské návyky. Je potřeba striktně dodržovat RAII (to bolí). Je potřeba změnit způsob destrukce polí (to bolí ještě víc), protože co by se mělo dít, když při destrukci pole dojde k výjimce u pátého prvku z deseti? Správně nic, destrukce by měla pokračovat, pouze v případě, že by došlo k další výjimce, je to chyba double exception. Zkuste tohle v STL!!!!

A to je třetí a nejzásadnější problém. Spousta standardních knihoven a překladačů s tím má prostě problém.  Asi z toho důvodu jsou výjimky v destruktorech v C++11 by default zakázány (a volají automaticky terminate) ale naštěstí je lze explicitně u jednotlivých objektů povolovat.

Nicméně dá se to nádherně využívat.

Ještě k řešení double exception
Kód: [Vybrat]
~Object() { try {
    ....
} catch (...) {
 //pokud leti vyjimka, neni dobry napad hazet dalsi
  if (!std::uncaught_exception())  throw;
}}


283
Odkladiště / Re:Bezpečný přenos hesla/klíče
« kdy: 04. 02. 2014, 16:04:25 »
No ale ta centrální evidence má zase jeden nějaký frontend, na který se leze typicky přes potenciálně děravý web. A tam si prostě zupdatuju, kolik peněz „ve skutečnosti“ mám a tedy kolik mi můžeš vyplatit.

No to bys právě neměl. Web já osobně považuju za stejně bezpečný jako kdyby to byla aplikace běžící uživateli na desktopu. Mimochodem, celý systém se bude ovládat výhradně přes api, webkovky budou pouze api používat, api bude i veřejné.

Hlavně - proboha!!! - do žádného v praxi nasazovaného řešení, zvláště, má-li operovat s nějakými daty podléhajícími právní ochraně, nezkoušejte procpat nějaké své tvůrčí pokusy z oblasti kryptografie! Držte se zásady, že řešení, jež nebylo dostatečně otestováno - jednak profíky v labech, jednak v praxi dostatečně motivujícími výzvami typu "kdo to první zlomí, získá $500k" - jest považovati a priori za nebezpečné!

Proč myslíš, že sem chodím se ptát. Nehledám vlastní řešení, hledám existující řešení. Ale nemůžeš to nikdy postavit z toho co už je, protože jednak je to vývoj nové věci, dále je to otázka ceny (prostě nemůžeš zabezpečit platební systém, kde se bude točit maximálně 1mil korun systémem za 10mil kourun) a pak je také dobré na místě opatrnost, protože standardní věci mají také standardní prolamovací protějšky. Neznáš bohužel míru s jakou uvažuju v projektu použít standardní ověření postupy (v labech) a kde bude mé vlastní autorské dílo.

284
Odkladiště / Re:Bezpečný přenos hesla/klíče
« kdy: 04. 02. 2014, 14:16:27 »


Ježiš, tohle dopadne. Asi bylo málo vyhackovaných směnáren. (osobně bych si při současném stavu počítačové bezpečnosti nic takového provozovat netroufl)

Jediné, co může pomoct, je mít více agentů a na každém jenom část částky (takže při napadení neukradne všechno)

Což o to, s tím počítám. Systém právě počítá s nasazením vícero agentů na různé lokality (dokonce se uvažuje na různé operační systémy a platformy) a do cloudů. Každý by měl držet jen část coinů a navíc určité procento přelejvat do cold walletu, ke kterým bude mít přístup pouze člověk. Dokonce lze snad dokázat, že pokud budu držet živé rezervy řádově v pár desítek procent vůči cold walletu, nebude vůbec potřeba coiny z cold walletu přelejvat zpět (viz systém částečných rezerv v bankách).

Napadení masteru je samozřejmě single point of failure, na druhou stranu, je dost problém to navrhnout jinak, když je potřeba vést centrální evidenci. Samozřejmě, že agenti nebudou chodit přímo na mastera, ale na některé z jeho slavů, který některé požadavky bude přeposílat na svého mastera, takže útočník bude muset prolomit víc míst. Samotný master nebude sedět přímo na internetu.

Výplatu lze také omezit pouze na adresy registrované v centrální evidenci. Lze to zařídit tak, že jeden slave nařídí agentovi vyplatit coiny uživateli 12345 a agent se půjde zeptat jiného slave, kdo je to uživatel 12345 a jestli má na virtuálním účtu skutečně tolik peněz.

To už je trochu offtopic.

počká, až přijde heslo z mastera, a klíč si prostě zkopíruje.

Kolik je úspěšných útoků nasazení trojského koně na server, který je schopen poslouchat a čekat na klíč (zabezpečenou komunikaci). Dejme tomu, že agent je standardní linuxový počítač s firewallem pouze ven a uvnitř běží aplikace jako binárka pod běžným userem, který sepravidelně připojuje na mastera. Pokud nepřekonám firewall, musím se dovnitř vlámat nějak přes ten request, třeba pomocí buffer overun tam vecpat vlastní kód. Za předpokladu, že donutím agenta, aby šel na můj nepřátelský server. Spíš jsem uvažoval o tom, že někdo okopíruje databázi, případně udělá dump celého stroje, nějaký pracovník telehausu nebo správce cloudu.

285
Odkladiště / Re:Bezpečný přenos hesla/klíče
« kdy: 04. 02. 2014, 12:58:27 »
Dobře, tak ještě se to pokusím vysvětlit jinak.

Problem je, že provedení výplaty v libovolném systémů Xcoinů  potřebuju privátní klíč, který si musím držet v počítači, který výplaty provádí (právě od toho má být ten agent, aby to nesedělo přímo na masterovi). A mé úvahy se stále točí kolem rizik. Je zřejmé, že privátní klíče musí být chráněny heslem ... ať už pomocí dlouhé passphrase, nebo i nějakou formou šifrování s pomalým dešifrováním. Problém je, že to heslo nemůže být jednorázové.

Mě jde o to, aby útočník pokud ovládne jednu nebo druhou stranu, neměl možnost provést výplatu. Chtěl bych docílit tohoto
 * Útočník pokud leakne data z agenta (předpokládám, že to bude jeho první cíl) nebude mít nikdy kompletní passphrase k odemčení privátních klíčů
 * Útočník, pokud leakne data z  mastera , tak zase nebude mít ty privátní klíče.
 * Ǔtočník se může pokusit ovládnout agenta na dálku tím, že se bude vydávat za jeho mastera (ale stále musí znát passphrase od původního mastera).

Agent by měl použít passphrase k odemčení klíče a podepsání transakce a pak to zaslané heslo přepsat náhodnými znaky (a doufat, že se to mezitím někam neodswapovalo. A mne šlo o to, jak nejlépe zasílat tu passphrasi. Když si tam zřídím HTTPS s certifikátem, který bude agent ověřovat aby tím vyloučil man-middle-attack, je přijatelné to heslo do odpovědi mastera dávat v plaintextu? Mě to nepřišlo jako bezpečné.

Samozřejmě že ty strany může ovládnout na živo, ale tenhle typ ovládnutí předpokládá, že se vláme to serveru a získá v něm nějaká významná práva (třeba roota), nebo že bude sedět přímo u stroje. Tohle zabezpečení bych řešil na jiných úrovních.




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