Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: sobol 04. 02. 2014, 19:14:38

Název: C++ a výjimka v destruktoru
Přispěvatel: sobol 04. 02. 2014, 19:14:38
Dobry den, puvodne jsem si chtěl zjistil, co se deje, když nastane výjimka v destruktoru a tak jsem si napsal kratky kod, který by mel na stdout vypisovat, co se zrovna deje. Zkrachoval jsem ale na tom, ze vypisovani vůbec nefunguje. Je, prosim, chyba v mem kodu? Dekuji

Kód: [Vybrat]
#include "stdafx.h"
#include <iostream>

using namespace std;

class A
{
A()
{
cout << "konstruktor";
}

~A()
{
cout << "destruktor";
}
};

class B
{
B()
{
cout << "konstruktor B";
}

~B()
{
cout << "destructor B";

throw new int(5);
}
};

int _tmain(int argc, _TCHAR* argv[])
{
int i = 1;
if (i=1)
{
A a();
B b();
}
/*
try
{

A a();
B b();
}
catch (...)
{
cout << "err";
}
*/
char ch[10];
cin >> ch;

return 0;
}
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: tadeas 04. 02. 2014, 19:46:03

S těmito úpravami (plus pár drobností jako int main) mi to funguje.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: tadeas 04. 02. 2014, 19:47:36
Ještě přidám ten upravený, funkční kód.
Kód: [Vybrat]
#include <iostream>

using namespace std;

class A
{
    public:
    A()
    {
        cout << "konstruktor";
    }

    ~A()
    {
        cout << "destruktor";
    }
};

class B
{
    public:
    B()
    {
        cout << "konstruktor B";
    }

    ~B()
    {
        cout << "destructor B";

        throw new int(5);
    }
};

int main(int argc, char* argv[])
{
    int i = 1;
    if (i == 1)
    {
        A a;
        B b;
    }

    return 0;
}

Kód: [Vybrat]
prg-mpskl:prog tmoravec$ g++ destructor.cpp
prg-mpskl:prog tmoravec$ ./a.out
libc++abi.dylib: terminating with uncaught exception of type int*
konstruktorkonstruktor Bdestructor BAbort trap: 6
prg-mpskl:prog tmoravec$
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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;
}}

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 04. 02. 2014, 22:51:44
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.

Dealokace se provede vždy.

ISO C++ 2011 5.3.5p7:

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

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!!!!

V STL to nejde, protože clear je nothrow.

ISO C++ 2011 23.2.1p11:
[…] no erase(), clear(), pop_back() or pop_front() function throws an exception […]

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.

No, je to hlavně z toho důvodu, že výjimky v destruktorech jsou tak ošidné, že lidé uvnitř ISO C++ se dohodli na tom, že to má být povolené jenom tam, kde programátor explicitně uvede, že ví, co dělá.

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

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

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

Je to trochu odlišné, protože to chytá i destruktory členů, ale ty by stejně neměly vyhazovat při uncaught_exception, takže výsledek je v podstatě stejný.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 04. 02. 2014, 23:25:45

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á.

> std::uncaught_exception

Je hack, který přináší vlastní řadu problémů. Např. při stack unwindingu může někde v hloubi destruktoru vyletět výjimka, která se o kousek dál chytne. Házející kód to ale neví, takže se musí spolehnout na uncaught_exception, která samozřjmě vrací true. Tohle zneškodní výjimky i tam, kde můžou normálně lítat. Více zde : http://www.gotw.ca/gotw/047.htm

Shrnutí

Pokud budeš házet výjímky ven z destruktorů, počítej s problémy. Ty prostě budou ať použiješ jakékoliv geniální triky. Navíc jsou to málo vykonávané kusy kódu, takže se ty problémy podle zákona schválnosti nejspíš projeví ve chvíli kdy bude šéf předvádět program důležitému zákazníkovi. Opravdu to nejde udělat jinak?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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).

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: hawran diskuse 05. 02. 2014, 08:41:58
http://www.stroustrup.com/bs_faq2.html#ctor-exceptions
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 09:12:30
Vyhazovat výjimky v destruktoru je zakázané snad ve všech coding standards, které jsem kdy viděl. Například v "101 Rules, Guidelines, and Best Practices":

Destructors, deallocation, and swap never fail.
Summary
Everything 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 09:52:10
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á.
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?

S házením výjimek z destruktoru mám i jeden spíš filosofický a sémantický problém:

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?

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. 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.

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.

Pokud si někdo myslí, že je lepší než tvůrci všech možných coding standards ...

Slyšel jsem krásný názor ve smyslu, že by se měl člověk snažit programovat tak, jak by programoval někdo daleko hloupější. Dává to smysl. Ve chvíli, kdy píšu něco na hranici svých možností, tak už nemám na to, řešit případné problémy.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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)

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 11:15:33
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.

No super, máme tady geniálního programátora... I kdyby to byla pravda (což si nemyslím), tak kód napsaný geniálním programátorem je prakticky nepoužitelný, protože ho "normální" programátoři nedokáží udržovat.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Flash 05. 02. 2014, 11:33:17
To je teď nějaká móda, označovat kódy za "příliš sofistikované" a doporučovat psát hloupé kódy, které sice budou neefektivní, ale rozumí jim každý jouda? Já to vidím tak, že když se někdo, kdo nezná jazyk do hloubky, pokouší upravovat kód od člověka, který ten jazyk zná jako svoje boty, tak na tom nezřídka pohoří a pak začne trousit kydy o nepoužitelném kódu, místo aby si uvědomil, že nepoužitelný není ten kód, ale on. Místo úchylných požadavků, aby profesionálové psali jako začátečníci, by se spíš měl zavřít do knihovny a to co mu není jasné, se doučit.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 12:30:00
To je teď nějaká móda, označovat kódy za "příliš sofistikované" a doporučovat psát hloupé kódy, které sice budou neefektivní, ale rozumí jim každý jouda? Já to vidím tak, že když se někdo, kdo nezná jazyk do hloubky, pokouší upravovat kód od člověka, který ten jazyk zná jako svoje boty, tak na tom nezřídka pohoří a pak začne trousit kydy o nepoužitelném kódu, místo aby si uvědomil, že nepoužitelný není ten kód, ale on. Místo úchylných požadavků, aby profesionálové psali jako začátečníci, by se spíš měl zavřít do knihovny a to co mu není jasné, se doučit.

Měl jsi někdy odpovědnost za nějaký projekt? Když budeš mít v týmu člověka, který píše nestandardně, povede to k horší kvalitě produktu, protože ostatní tomu kódu nerozumí / nechtějí rozumnět. Ve výsledku to dopadne tak, že se jeho kód nakonec musí vyhodit a napsat znovu normálně. Proto existují coding standards, aby všichni dodržovali nějakou kulturu a v kódu nebyly prasárny / špatně udržovatelné konstrukce. Znát jazyk jako svoje boty neznamená "můžu použít každou prasečinu, která v tom jde udělat".
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 12:36:16
Co třeba rezoluční funkci? ...
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í. No a říct o lidech jako Stroustrup, Alexandrescu a podobných, že jsou to úředníci co neradi myslí ...  ::)
Citace
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 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?
Citace
Jak? Co děláte, když vám ve standardním C funkce fclose() oznámí chybu? Zpravidla nic, zaignorujete to.
Když na tom nesejde, tak ano. Ale když na tom nesejde, tak můžu chyby odignorovat rovnou a nemusím nic házet.
Citace
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)
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é.
Kód: [Vybrat]
~WriteBuffer() {
try {
flush();
} catch (...) {
if (!std::uncaught_exception())
    throw;
}
}
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.

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
Kód: [Vybrat]
Transaction::~Transaction() {
try {
rollback();
} catch (...) {
//catch exception if we are in exception
if (!std::uncaught_exception()) throw;
}
}
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.
Citace
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ě.
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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: djicha 05. 02. 2014, 12:50:04
Zdravím, celá ta diskuze odbočila jinam než byl původní dotaz. Ale i tak je to krásné téma.
Bereme li v úvahu průměrné složení týmu, kdy jsou leadery jeden až dva senioři a zbytek juniorů v různém stádiu zkušeností, je největším problémem u pře-designovaného kódu právě zastupitelnost oněch seniorů. Jde v první řadě o peníze, takže by chybu měli být schopní opravit v čase stanoveným nějakým SLA i méně zdatní nebo zkušení. Pokud si senior bude honit triko tím že bude psát pro ostatní nepochopitelný kód, což si zatím nikdo koho znám nedovolil, je zaděláno na problémy především jemu, protože nebude moci ani na dovolenou.
Dalším faktem téhle práce bezesporu zkutečnost, že zákazníka pokud jím není jiná programátorská firma konkrétní implementace nezajímá, dokud to není pomalé, nebo to ukazuje něco co nemá. U nepřehledného kódu hrozí "nevíme co to dělá, na to se nesahá" tak se nalepí nad to nějaká další vrstva bordelu, a složitůra udržovatelnosti exponenciálně vzroste. Nesmí se prostě stát, že se řekne, to je šéfova funkce, bojím se ji opravit.
Navíc sebe vybavenější knihovna nenahradí opravdovou práci.
Ono bohatě stačí když se používá jiná logika u ternárního operátoru
Object o = i == null ? null : 1; nebo
Object o = i != null ? 1 : null;, význam je stejný, ale čitelnost a srozumitelnost se na první pohled velmi liší.
Je to konvencích a pokud se je naučí používat i junioři nebudou pak mít problém pochopit to co napsal senior.

Co se týče dokumentace, je dle mého názoru lepší když je kód dokumentovaný už tím jak je napsaný, ona dokumentace jakého koliv charakteru nemá šanci na dlouhodobé přežití.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 12:54:34
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í!
Občas zdrojáky STL čtu a dá se to pochopit. Samozřejmě, že to není psané úplně čitelně, ale to má dobrý důvod. Třeba normální názvy proměnných si autoři nemůžou dovolit kvůli preprocesoru.

Rozhranní STL (s přihlédnutím ke kompatibilitě a podobným věcem) je velice elegantní. Samozřejmě, že se najdou diskutabilní místa, ale celkově je tam těch prasečin docela málo.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: aaa 05. 02. 2014, 12:57:04
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
Proc to zastavovat u druhe vyjimky? Co kdyz nastane k-ta vyjimka? Zavedme si funkci on_kth_exception(int k)!

A vazne - ohackovavat v jazyku neco, co programator nezvladne osetrit sam, to je cesta do pekel.

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.
A co udela ten exception handler? Proc se nemuze vyjimka zachytit v destruktoru a rovnou obslouzit?

Jak? Co děláte, když vám ve standardním C funkce fclose() oznámí chybu? Zpravidla nic, zaignorujete to.
Proc se tedy snazite neignorovat chybu (vyjimku) v destruktoru, ale vymyslite nezmysly, jak je chytat?

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)
A co kdyz se to nepovede? Co s vyjimkou? Ignorujete ji? Nac pak delate rethrow?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 05. 02. 2014, 12:58:59
Jak řešit vyhození výjimky při unwindingu jiné? Potichu ignorovat původní, nebo novou výjimku? Nebo je nějak poskládat?

Ignorovat novou výjimku, protože je pravděpodobně zanesená. Ten objekt se totiž v tu chvíli destruuje kvůli té původní výjimce.

Pokud ale chcete být opravdu přesný, tak byste asi použil std::current_exception() a přidal do vyhozené výjimky tuhle další.

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?

Smazání objektu bez memory leaku je také konzistentní stav. Po vyhození výjimky z destruktoru se samozřejmě dostanete před začátek té chybné operace, ta totiž spočívá ve všem od konstruktoru toho objektu až po jeho destruktor.

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. 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.

A co se udělá v případě, kdy ten destruktor tu výjimku nevyhodí? Něco jako kdyby se Wordu při vypínání nepodařilo souboru uložit a ani to neohlásil. Jinak ukládání dat v destruktoru bych používal jen výjimečně a pokud to má smysl (např. pro memory-cached objekty, ale rozhodně ne pro dokumenty, u kterých není jasné, jestli to uživatel ukládat chce nebo ne).

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.

Vzhledem k tomu, že C++ nemá finally, je těch případů docela dost. Vlastně všechny, kdy byste v Javě použil finally.

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í!

Které STL? ;) Třeba GNU ISO C++ library mi přijde docela srozumitelná.

Co se týče dokumentace, je dle mého názoru lepší když je kód dokumentovaný už tím jak je napsaný, ona dokumentace jakého koliv charakteru nemá šanci na dlouhodobé přežití.

Proto existuje Doxygen :)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 13:15:09
Vzhledem k tomu, že C++ nemá finally, je těch případů docela dost. Vlastně všechny, kdy byste v Javě použil finally.
Ono to házení z finally v Javě má taky svůj příděl problémů. Jestli se nepletu, tak ta nová výjimka potichu přeplácne tu starou, ale tady si jistý nejsem.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 13:28:54
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.

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ů?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 13:49:55
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.

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ý.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 05. 02. 2014, 13:59:07
Vzhledem k tomu, že C++ nemá finally, je těch případů docela dost. Vlastně všechny, kdy byste v Javě použil finally.
Ono to házení z finally v Javě má taky svůj příděl problémů. Jestli se nepletu, tak ta nová výjimka potichu přeplácne tu starou, ale tady si jistý nejsem.

Jj, je to tak. Ale jednou se to dostalo do standardu, tak už se nedá nic dělat. Asi i proto se ISO C++ snaží vyhnout výjimkám v destruktorech.

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ý.

To jste asi ještě nečetl porovnání výkonu iostream a fread :)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 14:04:09
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.
Citace
Jsou to lidé. Ne bohové...
Ale už to nejsou úředníci, co neradi myslí, co?  ;)
Citace
Stroustrup ... Tak jsem ho začal ignorovat. Tím ho nechci schazovat, jen mi úplně jeho styl nesedl
A tohle si návrháři C++ prostě dovolit nemůžou. Prostě nemůžou některé požedavky prostě ignorovat.
Citace
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.
A ten důvod, proč to předtím bezpečně a bez výjimek zdestruovat nešlo, zmizí jak?
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
Kód: [Vybrat]
if(uncaught_exception())
  nejak_se_te_chyby_zbav_at_neprudi()
Zalogování chyby je pro vývojáře a ne pro uživatele.
Citace
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.
Já ale psal, že VÁM některé výjimky projdou. Já se s nimi vypořádávám tak, že do destruktoru necpu to, co tam není třeba. Díky tomu nemusím testovat, jestlivýjimku hodit nebo ignorovat, jak to děláte vy.
Pokud je mi při úklidu jedno, jestli flush skončil s chybou nebo ne, pak je mi i jedno jestli vůbec proběhl.
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.
Citace
Já jsem neříkal, že jí ignoruju, já jsem říkal, že jí nepoužívám.
Ani já nepsal, že ignorujete STL. Já říkal, že ignorujete všechny požadavky na ni, které nepotřebujete vy sám a díky tomu jste ji shrnul jako od blbců pro blbce.
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
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 14:06:10
To jste asi ještě nečetl porovnání výkonu iostream a fread :)

Ale jo četl, nicméně pokud se v performance critical kódu používají streamy, tak je něco v principu špatně, protože jsou opravdu pomalé.
Mimochodem pokusů udělat lepší (rychlejší, přenositelnější...) implementaci STL už bylo hodně a nikdy nevzniklo nic světoborného, nejlíp z toho asi vyšel  STLport:
http://stlport.sourceforge.net/FAQ.shtml

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 14:11:13
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)

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++ :).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: lulik 05. 02. 2014, 14:23:31
Ondrej Novak - to je ten programator s patnactiletou praxi, co pise derave webove aplikace, nema zakladni prehled o kryptografii a chtel by vytvorit skvele zabezpecenou pevnost pro obchodovani s kryptomenou?

hmm, od nej bych si dal rict :)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 14:33:00
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."
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 05. 02. 2014, 14:38:27
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.

Proč to dělat jednoduše, když to jde složitě, že. Nakonec takové goto má taky svoje kouzlo, kdo potřebuje nějaké destruktory :)

A ten důvod, proč to předtím bezpečně a bez výjimek zdestruovat nešlo, zmizí jak?

Nijak nezmizí. Zásadní rozdíl je v tom, že teď výjimka vyletí, zatímco předtím se to tiše ignorovalo.

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.

Tak to je asi std::uncaught_exception náhodný generátor.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 05. 02. 2014, 14:44:40
Ale jo četl, nicméně pokud se v performance critical kódu používají streamy, tak je něco v principu špatně, protože jsou opravdu pomalé.

Pěkné, důkaz kruhem v jedné větě :)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 14:53:50
Tak to je asi std::uncaught_exception náhodný generátor.
Není to náhodný generátor. Jen občas vrací true i když by výjimky šly úplně normálně házet. uncaught_exception neřeší, jak hluboko jsme od toho destruktoru, ze kterého ta výjimka nesmí vyletět. 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.

Prostě destruktory, které chyby zásadně nehlásí mi přijdou bezpečnější než ty, které je obvykle hlásí. Stejně se na to spolehnout nemůžu a aspoň nemám iluzi bezpečí.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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".
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 15:09:22
Pěkné, důkaz kruhem v jedné větě :)

Asi takhle, když zjistím, že jsou streamy přiliš pomalé, mám dvě možnosti:
1) Místo streamů použít něco jiného rychlejšího (stdio)
2) Napsat si vlastní rychlejší streamy
Já považuju za lepší variantu 1, pan Novák 2. Psát vlastní streamy nechci, protože se duplikuje funkcionalita STL streamů a vznikne hodně nového kódu, který se musí udržovat, což stojí čas a peníze. Navíc to zrychlení je vždycky za nějakou cenu (autoři STL nejsou úplně hloupí a nepíšou záměrně pomalý kód). Takových pokusů udělat něco rychleji nebo lépe než STL už byla celá řada a i když vznikly dobré věci (třeba loki library), mají omezené použití (lidi to neznají nebo tomu nevěří, protože to není dost používané, nebo si nechtějí do projektu zatahovat další kód).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 15:11:08
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.
Já nemám nic proti tomu, zkusit v tom destruktoru ten buffer flushnout. Podle mě jen nemá cenu tu výjimku házet, protože se na to stejně nedá spolehnout.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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á.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 05. 02. 2014, 15:19:45
Není to náhodný generátor. Jen občas vrací true i když by výjimky šly úplně normálně házet. uncaught_exception neřeší, jak hluboko jsme od toho destruktoru, ze kterého ta výjimka nesmí vyletět. 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.

Nějak nechápu, proč by měl ve chvíli, kdy se hledá handler pro mnohem vážnější výjimku, mělo zajímat, jestli prošel flush, když nadřazený objekt je stejně rozhozený.

Asi takhle, když zjistím, že jsou streamy přiliš pomalé, mám dvě možnosti:
1) Místo streamů použít něco jiného rychlejšího (stdio)
2) Napsat si vlastní rychlejší streamy
Já považuju za lepší variantu 1, pan Novák 2. Psát vlastní streamy nechci, protože se duplikuje funkcionalita STL streamů a vznikne hodně nového kódu, který se musí udržovat, což stojí čas a peníze. Navíc to zrychlení je vždycky za nějakou cenu (autoři STL nejsou úplně hloupí a nepíšou záměrně pomalý kód). Takových pokusů udělat něco rychleji nebo lépe než STL už byla celá řada a i když vznikly dobré věci (třeba loki library), mají omezené použití (lidi to neznají nebo tomu nevěří, protože to není dost používané, nebo si nechtějí do projektu zatahovat další kód).

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 :)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 15:26: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/
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 15:26:32
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?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 15:42:27
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
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 05. 02. 2014, 16:16:19
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?

Tohle má ten drobný detail, že pokud fopen selže (nebo někdy zavoláte reset), tak se fclose zavolá na nullptr. SIGSEGV.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Obvious 05. 02. 2014, 16:27:18
Ondrej Novak - to je ten programator s patnactiletou praxi, co pise derave webove aplikace, nema zakladni prehled o kryptografii a chtel by vytvorit skvele zabezpecenou pevnost pro obchodovani s kryptomenou?

hmm, od nej bych si dal rict :)

+1
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Curious 05. 02. 2014, 16:37:04
Pane Novak, jen me tak napada. Minule jste popisoval svoji lasku k open source. Nemate nekde tu vasi LightSpeed STL-killer knihovnu nekde ke shlednuti?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. ReadWholeForum 05. 02. 2014, 16:58:35
To teda nemam (Novák)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 21:26:44
Tohle má ten drobný detail, že pokud fopen selže (nebo někdy zavoláte reset), tak se fclose zavolá na nullptr. SIGSEGV.
Jo, to je pravda. Pozapomněl jsem na to, že fopen kontroluje null jen v některých implementacích. Chtěl jsem napsat ilustrační příklad a nedomyslel jsem ho. Osobně používám unique_ptr, ale nechtělo se mi sem psát ten funktor.

Navrch je tam stejný problém o kterém se tu stále diskutuje - nekontroluju návratovou hodnotu a ve vzácných případech to může být problém.

Co mě ale fascinuje je, že se Ondra Novák chytl na ten nejmenší problém s tímhle kódem. Ne, řešit jednu dynamickou alokaci paměti u diskových operací mi prostě hlava nebere. I když zanedbám pomalost disku, tak ovladač tam bude alokovat, operační systém tam bude alokovat, standardní knihovna tam bude alokovat a já se mám alokaci vyhýbat abych nemrhal výkonem.

Š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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 21:38:01
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í.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 22:07:32

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
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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š?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: PotatoJesus 05. 02. 2014, 22:22:27
Nevzpomínám si, že bych v destruktoru někdy chrlil výjimky  :-\

Ani jsem netušil, že to jde.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 22:25:49
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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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ě.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 22:45:20
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.
Vlastně ani nesmí, pokud to má fungovat vždycky.
Citace
Ale to je jedno. Klidně si tam může specializovat vlastní operaci, která výjimku z destruktoru zaignoruje a pojede se vesele dál.
fault tolerant != fault ignorant
Citace
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 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?
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)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 05. 02. 2014, 23:09:17
Jo, to je pravda. Pozapomněl jsem na to, že fopen kontroluje null jen v některých implementacích. Chtěl jsem napsat ilustrační příklad a nedomyslel jsem ho. Osobně používám unique_ptr, ale nechtělo se mi sem psát ten funktor.

Takže si napíšu funktor na destrukci. Pak si napíšu funktor pro čtení dat, potom ještě pro bufferovaný zápis. A pak zjistím, že jsem tím zmršil zapouzdření, takže mám najednou bordel v tom, kdo má za co zodpovědnost, jelikož je ta implementace různě poházená po programu a bůhví v kolika kopiích (soukromě tomu přezdívám neverending bugfixing nightmare). A pak to celé refaktoruju a přepíšu do streamů. Anebo to můžu udělat rovnou ;)

Š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.

U realokace vektoru to řešit nepotřebujete, protože destruktory se budou volat až po té realokaci, takže rollback není potřeba, pole již bylo přesunuto a je konzistentní (navíc by ty objekty měly použít move konstruktory, takže nemají co destruovat a tedy ani vyhazovat výjimky). Ale chápu pointu. Je to celkem snadné, když víte, jak na to:
Kód: [Vybrat]
void Array::clear()
{
struct Deleter
{
Array & arr;
int index;

Deleter(Array & arr)
: arr(arr)
, index(arr.size())
{}

~Deleter()
{
destroy();
delete arr.data;
}

void destroy()
{
while (index > 0)
arr.data[--index].~T();
}
} deleter;

deleter.destroy();
}
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 23:11:23
To myslíš vážně, nebo si děláš srandu?
Tohle myslím naprosto vážně. Pokud přesun jednoho prvku může selhat tak po nepovedené realokaci prostě netuším, jestli jsou ty původní prvky rollbackuté všechny nebo jich pár zařvalo. A takový výsledek se hodí k jedinému - hodit do stoupy a začít znova.

Citace
Ale jde to zaručit, jen to nikdo nechce řešit...
Jo, je spousta řešení, která skoro fungují. Ale nějak chybí řešení, která fungují. A odignorovat všechny problematické výjimky taky není řešení.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 05. 02. 2014, 23:23:07
FYO reference counting u STL řetězců je pokud vím zakázaný, nebo aspoň silně nedoporučovaný.

Opravdu? Potom by asi bylo dobré autorům STL vysvětlit, že to dělají úplně špatně a nemají reference counting používat:
Kód: [Vybrat]
#include <string>
#include <iostream>

int main()
{
    std::string s1 = "a";
    std::string s2 = s1;
    std::cout << reinterpret_cast<const void*>(s1.c_str()) << std::endl;
    std::cout << reinterpret_cast<const void*>(s2.c_str()) << std::endl;
    s2[0] = 'b';
    std::cout << reinterpret_cast<const void*>(s1.c_str()) << std::endl;
    std::cout << reinterpret_cast<const void*>(s2.c_str()) << std::endl;
    return 0;
}
g++ -O2 main.cc ; ./a.out
0xa35028
0xa35028
0xa35028
0xa35058
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 05. 02. 2014, 23:29:06
Takže si napíšu funktor na destrukci. Pak si napíšu funktor pro čtení dat, potom ještě pro bufferovaný zápis. A pak zjistím, že jsem tím zmršil zapouzdření, takže mám najednou bordel v tom, kdo má za co zodpovědnost, jelikož je ta implementace různě poházená po programu a bůhví v kolika kopiích (soukromě tomu přezdívám neverending bugfixing nightmare). A pak to celé refaktoruju a přepíšu do streamů. Anebo to můžu udělat rovnou ;)
Takže si napíšu funktor na destrukci a ... a tím to končí. Jaké další funktory jsou potřeba? Na otestování návratové hodnoty dalších funkcí a případné vyhození výjimky není třeba žádný funktor. A taky není třeba balit každý fwrite zvlášť.
Citace
U realokace vektoru to řešit nepotřebujete, protože destruktory se budou volat až po té realokaci, takže rollback není potřeba, pole již bylo přesunuto a je konzistentní (navíc by ty objekty měly použít move konstruktory, takže nemají co destruovat a tedy ani vyhazovat výjimky). Ale chápu pointu. Je to celkem snadné, když víte, jak na to:
Kód: [Vybrat]
void Array::clear()
{
struct Deleter
{
Array & arr;
int index;

Deleter(Array & arr)
: arr(arr)
, index(arr.size())
{}

~Deleter()
{
destroy();
delete arr.data;
}

void destroy()
{
while (index > 0)
arr.data[--index].~T();
}
} deleter;

deleter.destroy();
}
Nevím, jakou relevanci má tenhle kód k mému dotazu. Nic se tam nerealokuje, jen likviduje. Samozřejmě, že umím zrušit všechny prvky vektoru a odignorovat chyby.
Co neumím je vektor přealokovat, pokud může házet jak kopírovací konstruktor, tak destruktor. Jo, umím to přesunout jako by ty destruktory neházely a odignorovat, co vyhodí. Jen tu výhodu proti noexcept destruktorům v tom nejak stále nevidím. No ale aspoň nepřijdu o žádný prvek, pokud vyletí výjimka. To je proti kódu Ondřeje Nováka jednoznačné plus.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 06. 02. 2014, 00:12:56
Takže si napíšu funktor na destrukci a ... a tím to končí. Jaké další funktory jsou potřeba? Na otestování návratové hodnoty dalších funkcí a případné vyhození výjimky není třeba žádný funktor. A taky není třeba balit každý fwrite zvlášť.

Například pro načtení jednoho řádku do stringu nebo i pro zápis stringu, když nechcete pořád psát fwrite(str.c_str(), str.size(), 1, *file).

Nevím, jakou relevanci má tenhle kód k mému dotazu. Nic se tam nerealokuje, jen likviduje. Samozřejmě, že umím zrušit všechny prvky vektoru a odignorovat chyby.

Já ty chyby ale neignoruji ;)

Co neumím je vektor přealokovat, pokud může házet jak kopírovací konstruktor, tak destruktor. Jo, umím to přesunout jako by ty destruktory neházely a odignorovat, co vyhodí. Jen tu výhodu proti noexcept destruktorům v tom nejak stále nevidím. No ale aspoň nepřijdu o žádný prvek, pokud vyletí výjimka. To je proti kódu Ondřeje Nováka jednoznačné plus.

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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í)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 06. 02. 2014, 02:12:18
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.

Selhat ale může i rollback. Pokud nejdříve všechno zkopíruji a až poté všechno zdestruuji, tak mám jistotu, že budu mít vždy konzistentní stav: buď ten původní (pokud selže kopírování) nebo ten nový (pokud selže destruování). Sice je to málo pravděpodobné, ale má to lepší exception safety pro případ, když už to nastane (strong místo basic).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 06. 02. 2014, 08:38:48
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.
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.
Citace
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í)
Koukám, že si z STL celkem konzistentně vybíráš věci, které se úplně nepovedly a ty povedené naopak ne.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 06. 02. 2014, 08:58:14
Já ty chyby ale neignoruji ;)
To já ne, to ten destruktor? Zastávám názor, že házení výjimek jen pokud to ničemu nevadí není řešení.

Zdůvodním :
1) Pokud mi destruktory neházejí, vím, že se o chybách uvnitř destruktorů nedovím. Pokud se chci spolehnout, že všechno klaplo, musím to před zrušením objektu zkontrolovat.
2) Pokud mi destruktory házejí, tak vím že se o chybách občas taky nedovím. Pokud se chci spolehnout, že všechno klaplo, tak to musím taky zkontrolovat před zrušením objektu. A navíc musím samozřejmě chytat ty výjimky, takže místo jednoho místa ošetřuju stejnou chybu na dvou místech. A to se prostě vyplatí.

Samozřejmě že můžu zapojit svou kreativitu a říct si že ty výjimky které se mi nelíbí jsou pravděpodobně stejně zavlečené, zbytečné a tím pádem je můžu úspěšně ignorovat. A svět je zase sluníčkový...
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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é
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 06. 02. 2014, 12:26:53
To já ne, to ten destruktor? Zastávám názor, že házení výjimek jen pokud to ničemu nevadí není řešení.

Co? Ukazoval jsem řešení, které zdestruuje dané pole, přičemž pokud dojde k výjimce, tak jej stále zdestruuje a zároveň ta výjimka vyletí.

Zdůvodním :
1) Pokud mi destruktory neházejí, vím, že se o chybách uvnitř destruktorů nedovím. Pokud se chci spolehnout, že všechno klaplo, musím to před zrušením objektu zkontrolovat.
2) Pokud mi destruktory házejí, tak vím že se o chybách občas taky nedovím. Pokud se chci spolehnout, že všechno klaplo, tak to musím taky zkontrolovat před zrušením objektu. A navíc musím samozřejmě chytat ty výjimky, takže místo jednoho místa ošetřuju stejnou chybu na dvou místech. A to se prostě vyplatí.

Anebo se na výjimky vykašlu rovnou, protože jinak tu chybu stejně ošetřuju na dvou místech. A to se prostě vyplatí.

Samozřejmě že můžu zapojit svou kreativitu a říct si že ty výjimky které se mi nelíbí jsou pravděpodobně stejně zavlečené, zbytečné a tím pádem je můžu úspěšně ignorovat. A svět je zase sluníčkový...

Anebo můžu přestat brát drogy a trochu se nad tím zamyslet ;)

Pokud se destruuje vnořený objekt, když jeho nadřazený objekt vyhodil výjimku (to je ten případ, kdy výjimku ignoruju), tak je mi úplně jedno, jestli ten vnořený objekt selže nebo ne. Ta operace na nadřazeným objektem už selhala, takže i kdyby ta chyba na vnořeném objektu nebyla zavlečená, stejně mě nezajímá.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 06. 02. 2014, 12:36:57
Pokud se destruuje vnořený objekt, když jeho nadřazený objekt vyhodil výjimku (to je ten případ, kdy výjimku ignoruju), tak je mi úplně jedno, jestli ten vnořený objekt selže nebo ne. Ta operace na nadřazeným objektem už selhala, takže i kdyby ta chyba na vnořeném objektu nebyla zavlečená, stejně mě nezajímá.

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á.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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í.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 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í.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 06. 02. 2014, 13:46:47
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á.

Pokud autopilot spadne, tak je v tom letadle něco úplně špatně (třeba došlo ke flameoutu), takže jestli je špatně i výškovka, je v tu chvíli celkem maličkost.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 06. 02. 2014, 14:33:24
Pokud autopilot spadne, tak je v tom letadle něco úplně špatně (třeba došlo ke flameoutu), takže jestli je špatně i výškovka, je v tu chvíli celkem maličkost.

Autopilot se normálně vypne třeba proto, že fouká silný nárazový vítr a nedokáže udržet letadlo v přímém letu. To je normální stav, pilotovi se zahlásí alam o vypnutí autopilota a ten převezme řízení. Pokud autopilot při ukončení nedokáže nastavit řídicí prvky do nějakého definovaného stavu, pilot by o tom měl vědět. On se to ale nedozví, protože Sten rozhodl, že tyhle výjimky už nejsou důležité, stačí reportovat jen jednu.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 06. 02. 2014, 14:57:21
Autopilot se normálně vypne třeba proto, že fouká silný nárazový vítr a nedokáže udržet letadlo v přímém letu. To je normální stav,

To ukazuje na chybné použití výjimky. Vadnou část jsem potrhnul.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: sobol 06. 02. 2014, 16:37:14
Jako prvotnímu dotazovateli si myslím, že i když je to možný, tak výjimky do destruktoru nepatří. To, že to jazyk umožní, nepovažuju za feature oop. Je to jako protimluv. Chtel jsem nasimulovat, co by se stalo a jak s tím naložit, kdyby k tomu došlo.

Neporozuměl jsem, že se mi ve VS2013 ani nevypíše, že došlo ke konstrukci objektu.

Honza
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 06. 02. 2014, 16:53:37
To ukazuje na chybné použití výjimky. Vadnou část jsem potrhnul.

Nikoliv, autopilot se vypne (bez výjimky), to je normální stav. V destruktoru klapek vznikne výjimka "nemůžu nastavit klapky" a poté vznikne v destruktoru výškovky výjimka "nemůžu nastavit výškovku". Pilot se dozví, že má blbě klapky, ale už se nedozví, že mu letadlo padá kvůli blbě nastavené výškovce. Jestli to přežije, tak bude proklínat programátora, který si myslí, že vyhazovat výjimky v destruktoru je v pohodě.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: kuka 06. 02. 2014, 16:57:42
Co když to bude aplikace autopilota a ten destruktor vnořeného objektu bude nastavovat výškovku do defaultní polohy
Pokud bude destruktor neco nastavovat, tak to je predevsim blbe navrzene.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 06. 02. 2014, 17:07:30
Pokud bude destruktor neco nastavovat, tak to je predevsim blbe navrzene.

S tím samozřejmně souhlasím a to vede na řešení:
Destruktor nedělá pokud možno nic sofistikovaného a ošetření chybových stavů patří mimo destruktor, z čehož vyplývá, že destruktor nevyhazuje výjimky :).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 06. 02. 2014, 17:29:18
Pokud bude destruktor neco nastavovat, tak to je predevsim blbe navrzene.

S tím samozřejmně souhlasím a to vede na řešení:
Destruktor nedělá pokud možno nic sofistikovaného a ošetření chybových stavů patří mimo destruktor, z čehož vyplývá, že destruktor nevyhazuje výjimky :).

Destruktor zejéma uklízí zdroje. A pokud se při úklidu zdrojů objeví chyba, tak za normálního běhu by měl o tom informovat výjimkou. Pokud se však objeví chyba při úklidu během stack unwind, tak taková výjimka už beztak nemá žádný smysl, protože bude z 99.9% souviset s již probíhajícím chybovým stavem.

Víc k tomu nemá co smysl psát. Myslím, že to je jasné a zřejmé. Jestli to děláte jinak, je to váš problém
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Tomáš Marný 06. 02. 2014, 17:46:18
To ukazuje na chybné použití výjimky. Vadnou část jsem potrhnul.
Jestli to přežije, tak bude proklínat programátora, který si myslí, že vyhazovat výjimky v destruktoru je v pohodě.

V softwaru pro použití v kritických oblastech, jako je třeba toto nebo řízení v průmyslu, se velmi mnoho věcí, jež jazyk umožňuje, zcela zakazuje používat - právě z důvodů, že je o tom možné vést takovéto debaty a důsledky jsou dosti nepredikovatelné. Takže nějaké vyhazování výjimek v destruktoru v SW letadla rozhodně nehrozí. :) Jen pro zajímavost - v SW pro řízení elektráren je zakázáno i pouhé přetěžování operátorů, natož takovéhle věci.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 06. 02. 2014, 17:53:54
Nikoliv, autopilot se vypne (bez výjimky), to je normální stav.

Jistě, ono je úplně normální, když se něco jen tak samo vypne a spustí to alarm. Nicméně vás chápu. Kdybyste totiž tohle řešil výjimkou (protože jde o výjimku, autopilot se ukončuje, protože to nezvládá, ne protože mu to řekl uživatel), nemohl byste vymýšlet tyhle komplikace :)

V destruktoru klapek vznikne výjimka "nemůžu nastavit klapky" a poté vznikne v destruktoru výškovky výjimka "nemůžu nastavit výškovku". Pilot se dozví, že má blbě klapky, ale už se nedozví, že mu letadlo padá kvůli blbě nastavené výškovce. Jestli to přežije, tak bude proklínat programátora, který si myslí, že vyhazovat výjimky v destruktoru je v pohodě.

V tom případě by měl destruktor autopilota vyhodit výjimku, že se nepodařilo nastavit původní stav, a případně tam přidat ty výjimky, co vyletěly. Něco jako dělá Java.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 06. 02. 2014, 18:03:21


V tom případě by měl destruktor autopilota vyhodit výjimku, že se nepodařilo nastavit původní stav, a případně tam přidat ty výjimky, co vyletěly. Něco jako dělá Java.

Dik, ted me napadlo, jak tohle realizovat v LightSpeed::Exception. Uz vim co narvat do vetve kdyz std::uncaught_exception je true. Podminkou akorat bude, ze vyjimka dedi std::exception
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ivorne 06. 02. 2014, 20:50:58
Já tedy zastávám názor, že výjimky obecně nejsou vhodné při snaze o vysokou odolnost proti chybám.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: sobol 06. 02. 2014, 20:51:46
Takovy flame, lidi jste silenci.:-)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ivorne 06. 02. 2014, 21:09:19
Takovy flame, lidi jste silenci.:-)

Tak ale je to poučný, docela rád jsem si to pročetl.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: RAII 06. 02. 2014, 21:22:08
No sakra, exception v DESTRUKTORU?? V C++ ? Todle si dělej v ňáký blbý javě nebo C#. TODLE je C++, v C++ se používá RAII, žádný GC který po tobě uklidí bordel za cenu cenného výkonu (GC je pro idioty, jen můj názor). Tím že pokurvíš destruktor výjimkou jdeš přímo proti principu RAII.
Destruktor prostě nesmí nikdy selhat, jinak ti utečou zabrané prostředky objektem (hmm exception se ti vyvolá při stack unwindingu a nebo když se dostane objekt out of scope... HMM ?). Kam asi ta výjimka propadne, někam dolu ... TAK MI PAK vysvětli jakou
hrůzou chceš tendle chaos napravit. Pokud potřebuješ exception v destruktoru, špatně si navrhl program a nebo si debil.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Trollslayer 06. 02. 2014, 21:42:46


trollove si vsimli nove diskuze. To brzo. Vsechno uz bylo popsano od tretiho prispevku dale. Staci cist.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 06. 02. 2014, 23:13:55
Já tedy zastávám názor, že výjimky obecně nejsou vhodné při snaze o vysokou odolnost proti chybám.

Naopak, stabilita mých aplikací výrazně stoupla, zejména serverů. Na každou vyhozenou výjimku někde čeká handler, který v nejhorším případě vrátí klientoví 500 Internal Server Error. Je to sice otrava (pro klienta), ale server se nesloží
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 06. 02. 2014, 23:36:00
Naopak, stabilita mých aplikací výrazně stoupla, zejména serverů. Na každou vyhozenou výjimku někde čeká handler, který v nejhorším případě vrátí klientoví 500 Internal Server Error. Je to sice otrava (pro klienta), ale server se nesloží

Tak to je super, klient nedostal to co chtěl, aplikace z jeho pohledu vůbec nefunguje, ale stabilita stoupla ;). A teď si představte, že klientem Vaší aplikace nebude Franta Surfař, ale pilot letadla, který dostane hlášku 500 Internal Aircraft Error. Tady by je z Vašich aplikací trefil šlak http://www.faa.gov/, ale naštěstí by ty Vaše výtvory nepřošly certifikací.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 00:27:35
Tak to je super, klient nedostal to co chtěl, aplikace z jeho pohledu vůbec nefunguje, ale stabilita stoupla ;). A teď si představte, že klientem Vaší aplikace nebude Franta Surfař, ale pilot letadla, který dostane hlášku 500 Internal Aircraft Error. Tady by je z Vašich aplikací trefil šlak http://www.faa.gov/, ale naštěstí by ty Vaše výtvory nepřošly certifikací.

Že tě to baví neustále trollovat. Považ, že na server chodí stovky až tisíce requestů za sekundu od zhruba stejného počtu userů. Takovej SIGSEGV nebo SIGABORT by nepotěšilo mnohem víc klientů, než jednoho s pětistovkou. Ale bohužel na světě je hodně takových jako jsi ty, co to nechápou
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: aaasss 07. 02. 2014, 01:15:21
Naopak, stabilita mých aplikací výrazně stoupla, zejména serverů. Na každou vyhozenou výjimku někde čeká handler, který v nejhorším případě vrátí klientoví 500 Internal Server Error. Je to sice otrava (pro klienta), ale server se nesloží
A preco by sa mal server zlozit, ked nieco nefunguje? Pri chybe nie je vzdy nutne vyhadzovat vynimku - vsak su aj Cckoidne riesenia chyby. Niekedy staci goto, inokedy je mozne vratit chybovy stav (ci uz hnusne cez zdielanu premennu - errno) alebo navratovou hodnotou alebo ulozenie niecoho na pointer dodany v parametroch.

Já tedy zastávám názor, že výjimky obecně nejsou vhodné při snaze o vysokou odolnost proti chybám.
V C++ asi mate pravdu - niekto nieco neodchyti a dopadne to zle. V Jave su na toto checked exceptions, ktore uzivatela nutia zohladnit vynimku.

Pri snahe o odolnost voci chybam sa niekedy hodi prinutit programatora, aby premennu pouzil - potom uz tak nepochybuje o tom, ci ju je treba naozaj pouzivat a testovat.
Neodolne voci chybam je nieco, co sa vola
Kód: [Vybrat]
int errno = action();
Odolnejsie voci chybam je volanie ako
Kód: [Vybrat]
action(&errno);
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 02:18:46
A preco by sa mal server zlozit, ked nieco nefunguje? Pri chybe nie je vzdy nutne vyhadzovat vynimku - vsak su aj Cckoidne riesenia chyby. Niekedy staci goto, inokedy je mozne vratit chybovy stav (ci uz hnusne cez zdielanu premennu - errno) alebo navratovou hodnotou alebo ulozenie niecoho na pointer dodany v parametroch.

protože
Kód: [Vybrat]

void readPage(Request *r) {
    char buffer[1000];
    FILE *f = fopen(r->pathname,"r");
    int i = fread(buffer, 1000,1,f);   //crash SIGSEGV
   //...
    fclose(f);

}

ale

Kód: [Vybrat]

void readPage(Request *r) {
    char buffer[1000];
    FileStream f(r->pathname); //exception - FileNotFound exception
    int i = f.read(buffer, 1000); 
   //...

}

... {
    try {
       readPage(r);
   } catch (FileNotFoundException &e) {
       errorPage(404);
   } catch (...) {
       errorPage(500);
   }
}

Jasně, Cckové řešení vyžaduje ošetřit návratovou hodnotu z fopen. A pak nějak vrátit chybový kód tomu volajícímu. Výjimka takové povinnosti eliminuje. Ať už funkce readPage dělá cokoliv (může být abstraktní, implementovaná v potomkovi), pokud vyhodí FileNotFoundException, pošlu na výstup status 404 Not found. Všimněte si také, že výjimkové řešení bude o několik řádek kratší. A v neposledním případě, výjimkové řešení je rychlejší, protože kód neobsahuje zbytečné testy na každém rozcestí, obsahuje jen vyhodnocení místě vzniku chyby a pak vyhodnocení při výjimce. Výjimky se totiž řeší bokem, mimo hlavní kód... tedy vrací se jinou cestou, než původní kód.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 04:28:16
Že tě to baví neustále trollovat. Považ, že na server chodí stovky až tisíce requestů za sekundu od zhruba stejného počtu userů. Takovej SIGSEGV nebo SIGABORT by nepotěšilo mnohem víc klientů, než jednoho s pětistovkou. Ale bohužel na světě je hodně takových jako jsi ty, co to nechápou

Troluješ ty, SIGSEGV nebo 500 vyjde pro uživatele úplně nastejno a když na serveru spadne worker, vyspawnuje se místo něho nový, takže to postihne taky jednoho uživatele. Mimochodem cílem by nemělo být "nějak to zbastlíme a když občas někdo dostane 500, tak je to jedno, ve statistiká se to přece ztratí", ale "vytvoříme robustní řešení schopné definovaně ošetřit a reportovat chybové stavy (i více, ne jen první)".
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 07:15:27


Troluješ ty, SIGSEGV nebo 500 vyjde pro uživatele úplně nastejno a když na serveru spadne worker, vyspawnuje se místo něho nový, takže to postihne taky jednoho uživatele. Mimochodem cílem by nemělo být "nějak to zbastlíme a když občas někdo dostane 500, tak je to jedno, ve statistiká se to přece ztratí", ale "vytvoříme robustní řešení schopné definovaně ošetřit a reportovat chybové stavy (i více, ne jen první)".

Prefork patri do minuleho stoleti. Dneska jsou servery threadove. Jejich nevyhodou je, ze kdyz spadne jeden thread, vezme s sebou ostatni. Vyhodou naopak je vyssi rychlost (razeni vlaken je snazsi nez procesu) a takova kompaktnost aplikace, jako ze jednotliva vlakna mohou sdilet data, mohou snadno spoustet a ridit joby a podobne. Klade to vyssi naroky na stabilitu cele aplikace, tedy aby kazdy problem byl osetren a zareportovan, protoze sestreleni threadoveho serveru muze byt dost fatalni.

Sorry ale navrhovat servery s tim, ze kdyz to spadne vyspawnujeme novyho workera neni hodne profesionalna.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 09:23:34
Prefork patri do minuleho stoleti. Dneska jsou servery threadove. Jejich nevyhodou je, ze kdyz spadne jeden thread, vezme s sebou ostatni. Vyhodou naopak je vyssi rychlost (razeni vlaken je snazsi nez procesu) a takova kompaktnost aplikace, jako ze jednotliva vlakna mohou sdilet data, mohou snadno spoustet a ridit joby a podobne. Klade to vyssi naroky na stabilitu cele aplikace, tedy aby kazdy problem byl osetren a zareportovan, protoze sestreleni threadoveho serveru muze byt dost fatalni.

Sorry ale navrhovat servery s tim, ze kdyz to spadne vyspawnujeme novyho workera neni hodne profesionalna.
Ale houby, normálně se to dělá takhle:
http://httpd.apache.org/docs/2.2/mod/worker.html
Mám kontrolu nad tím, kolik je workerů a kolik je threadů. Když spadne worker, vezme s sebou všechny thready, takže to ovlivní jen ty uživatele, které worker zrovna obsluhuje, což je číslo menší nebo rovno počtu threadů na workera.

Citace
Sorry ale navrhovat servery s tim, ze kdyz to spadne vyspawnujeme novyho workera neni hodne profesionalna.
Sorry, ale navrhovat server s tím, že crash jednoho threadu mi shodí celý server, není hodné profesionála.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 09:53:22
Citace
Sorry ale navrhovat servery s tim, ze kdyz to spadne vyspawnujeme novyho workera neni hodne profesionalna.
Sorry, ale navrhovat server s tím, že crash jednoho threadu mi shodí celý server, není hodné profesionála.
Profesionální programátorovi program nespadne. Nikdy. Program by neměl padat. Na výjimečnou situaci se reaguje výjimkou
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 09:59:11
Profesionální programátorovi program nespadne. Nikdy. Program by neměl padat. Na výjimečnou situaci se reaguje výjimkou

Profesionální programátoři používají v serverech mimo svého kódu i generické komponenty, ve kterých jsou bohužel známé i neznámé bezpečnostní chyby. Se zapnutým binary hardening, což je na serveru dobrý nápad, vede pokus o exploit na crash.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 10:47:04
Profesionální programátorovi program nespadne. Nikdy. Program by neměl padat. Na výjimečnou situaci se reaguje výjimkou

Profesionální programátoři používají v serverech mimo svého kódu i generické komponenty, ve kterých jsou bohužel známé i neznámé bezpečnostní chyby. Se zapnutým binary hardening, což je na serveru dobrý nápad, vede pokus o exploit na crash.

Pokud musi programátor používat i kód od amaterů, tak jistě máte pravdů, od toho mám SEH
(http://novacisko.blog.root.cz/2012/09/05/seh-v-linuxu-c/)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 10:55:05
Pokud musi programátor používat i kód od amaterů, tak jistě máte pravdů, od toho mám SEH
(http://novacisko.blog.root.cz/2012/09/05/seh-v-linuxu-c/)
V tom případě si napište i vlastní kernel, protože v něm jsou bezpečnostní chyby taky.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 11:48:12
Pokud musi programátor používat i kód od amaterů, tak jistě máte pravdů, od toho mám SEH
(http://novacisko.blog.root.cz/2012/09/05/seh-v-linuxu-c/)
V tom případě si napište i vlastní kernel, protože v něm jsou bezpečnostní chyby taky.
A teď o té červené karkulce.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 07. 02. 2014, 12:10:11
Troluješ ty, SIGSEGV nebo 500 vyjde pro uživatele úplně nastejno a když na serveru spadne worker, vyspawnuje se místo něho nový, takže to postihne taky jednoho uživatele. Mimochodem cílem by nemělo být "nějak to zbastlíme a když občas někdo dostane 500, tak je to jedno, ve statistiká se to přece ztratí", ale "vytvoříme robustní řešení schopné definovaně ošetřit a reportovat chybové stavy (i více, ne jen první)".

Pak nechápu, proč u Apache vůbec řešili nějaké chybové stavy a vracejí 500, když by mohli jednoduše padat. Ono to možná bude tím, že workery např. používají sdílenou paměť, a pokud vám worker spadne na SIGSEGV, tak taky už nemusí nový worker naběhnout vůbec.

SIGSEGV není definované ošetřování a reportování chyb.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: belzebub 07. 02. 2014, 12:15:29
Ja myslim ze byste si meli precist toto:

"Cleaner, more elegant, and harder to recognize"
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

krasne se tam ukazuje jak vyjimky poskytuji pouze ILUZI prehlednosti a jejich pouziti nemuzi byt vzdy prinosem.

A jinak k cele diskusi - pro me je to jen potvrzeni, ze C++ je jazyk, ktery si zaslouzi vyhynout a ze kdyz jsem pred 6 lety ve firme zamitl C++, ze jsem udelal dobre.

A dale ze "genialni programator", pan Novak, je solista, ktery nechape, ze existuji TEAMY, kde lide musi spolupracovat, prijimat nove lidi a zarucit vzajemnou zastupitelnost lidi.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 12:23:23
Ja myslim ze byste si meli precist toto:

"Cleaner, more elegant, and harder to recognize"
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx


Četl jsem. Předně "The Old New Thing" není žádná celebrita. Občas tam má zajímavé postřehy, občas tam má totální blbosti, jako v tomto případě. Nehledě na to, že ty příklady na špatné kódy má na konci v nějaké C#, v lepším případě.

Nemůžeš ignorovat vývoj.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 07. 02. 2014, 12:26:00
Ja myslim ze byste si meli precist toto:

"Cleaner, more elegant, and harder to recognize"
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

krasne se tam ukazuje jak vyjimky poskytuji pouze ILUZI prehlednosti a jejich pouziti nemuzi byt vzdy prinosem.

Ten článek má tři zásadní chyby:
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 12:26:39
Ja myslim ze byste si meli precist toto:

"Cleaner, more elegant, and harder to recognize"
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

Citace: The Old New Thing
(Yes, there are programming models like RAII and transactions, but rarely do you see sample code that uses either.)

Tahle poznámka vypovídá o všem. Článek nedoporučuju.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 12:33:17
A teď o té červené karkulce.

http://www.cvedetails.com/vulnerability-list/vendor_id-33/product_id-47/year-2013/Linux-Linux-Kernel.html
Podle Vaší logiky ten kernel od neschopných amatérských programátorů nemůžete používat a musíte si napsat vlastní, který bude samozřejmě bez chyb.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 12:37:30
Pak nechápu, proč u Apache vůbec řešili nějaké chybové stavy a vracejí 500, když by mohli jednoduše padat. Ono to možná bude tím, že workery např. používají sdílenou paměť, a pokud vám worker spadne na SIGSEGV, tak taky už nemusí nový worker naběhnout vůbec.

SIGSEGV není definované ošetřování a reportování chyb.
Nikdo netvrdí, že SIGSEGV je nějaké ošetření chyby, já jen říkám, že když uživatel dostane 500 je to pro něj úplně stejné fatální selhání, jak když proces spadne na SIGSEGV a uživatel nedostane nic. Sdílenou pamět mezi workery není nutné používat (a je lepší obejít se bez sdílené paměti, protože komplexnost obsluhy sdílené paměti je dost vysoká).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 07. 02. 2014, 12:43:05
Nikdo netvrdí, že SIGSEGV je nějaké ošetření chyby, já jen říkám, že když uživatel dostane 500 je to pro něj úplně stejné fatální selhání, jak když proces spadne na SIGSEGV a uživatel nedostane nic.

Pro toho konkrétního uživatele možná. Pro ty další, co používají daný worker, už ne.

Sdílenou pamět mezi workery není nutné používat (a je lepší obejít se bez sdílené paměti, protože komplexnost obsluhy sdílené paměti je dost vysoká).

Tak to řekněte Apachi. A nginxu. A lighttpd.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 13:27:55
Pro toho konkrétního uživatele možná. Pro ty další, co používají daný worker, už ne.
Těch uživatelů je konečný počet menší nebo roven počtu threadů.

Tak to řekněte Apachi. A nginxu. A lighttpd.
Nic mě nenutí sdílenou pamět mezi workery používat, můžu to udělat, ale nemusím.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Obvious 07. 02. 2014, 14:47:38
Bez pochyby neni sporu o tom, ze vyjimky v destruktoru prinasi vic skody nez uzitku.

PS: Co takhle udelat nejake hlasovani? 1. otazka: Je to dobre? 2. otazka: chteli byste s takovym kodem pracovat?

Mno a na zaver ...

http://c2.com/cgi/wiki?BewareOfExceptionsInTheDestructor
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Curious 07. 02. 2014, 14:50:40
Citace
Sorry ale navrhovat servery s tim, ze kdyz to spadne vyspawnujeme novyho workera neni hodne profesionalna.
Sorry, ale navrhovat server s tím, že crash jednoho threadu mi shodí celý server, není hodné profesionála.
Profesionální programátorovi program nespadne. Nikdy. Program by neměl padat. Na výjimečnou situaci se reaguje výjimkou

A co tohle? http://en.wikipedia.org/wiki/Fail-fast

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 07. 02. 2014, 15:03:34
Těch uživatelů je konečný počet menší nebo roven počtu threadů.

Ale větší nebo roven jedné. A pokud je větší, je určitě lepší, když se jednomu zahlásí 500, než když to celé spadne, ne?

Nic mě nenutí sdílenou pamět mezi workery používat, můžu to udělat, ale nemusím.

Nic kromě sdílení dat mezi workery, jako třeba statistiky nebo nonce pro autorizaci, ale to je maličkost. On to ten Apache asi používá jenom pro parádu ::)

Profesionální programátorovi program nespadne. Nikdy. Program by neměl padat. Na výjimečnou situaci se reaguje výjimkou

A co tohle? http://en.wikipedia.org/wiki/Fail-fast

Sice nesouhlasím s tím, že profesionálnímu programátorovi program nikdy nespadne, může spadnou třeba kvůli kosmickému záření nebo kvůli nějaké komponentě, kterou nemá ten programátor pod kontrolou, ale fail-fast je daleko blíže výjimce než SIGSEGV.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Curious 07. 02. 2014, 15:19:18
Sice nesouhlasím s tím, že profesionálnímu programátorovi program nikdy nespadne, může spadnou třeba kvůli kosmickému záření nebo kvůli nějaké komponentě, kterou nemá ten programátor pod kontrolou, ale fail-fast je daleko blíže výjimce než SIGSEGV.

Fail-fast is a property of a system or module with respect to its response to failures. A fail-fast system is designed to immediately report at its interface any failure or condition that is likely to lead to failure. Fail-fast systems are usually designed to stop normal operation rather than attempt to continue a possibly flawed process.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 15:24:30
Bez pochyby neni sporu o tom, ze vyjimky v destruktoru prinasi vic skody nez uzitku.
To jsou vaše závěry, ne moje

PS: Co takhle udelat nejake hlasovani? 1. otazka: Je to dobre? 2. otazka: chteli byste s takovym kodem pracovat?
OLOL  ;D

Mno a na zaver ...

http://c2.com/cgi/wiki?BewareOfExceptionsInTheDestructor

IMHO ten začátek je legrační
Citace
  • Objects with throwing destructors cannot be used in Standard containers
  • Objects with throwing destructors cannot even be composed safely
  • In the new C++11 standard all throwing destructors terminate the program, even when not UnwindingTheStack

Ad 1) nejdou použít protože to zakázali. Zakázali to, protože nejdou použít... tomu se říká cyklus
Ad 2) [citation needed]
Ad 3) Je obecně špatný krok v normě asi tak jako by vědci prohlásili, že kvantovou fyziku stejně nikdo nechápe, takže ji radši zrušíme.

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: belzebub 07. 02. 2014, 15:44:35
"Cleaner, more elegant, and harder to recognize"
Ten článek má tři zásadní chyby:
...

Netvrdim, ze je clanek bezchybny (taky je uz HODNE stary - coz ilustruje zminka o RAII jako o necem neobvyklem), ale me ve sve dobe primel hluboce se zamyslet nad vlastnim konceptem vyjimky, ktera je z principu urcita forma "nelokalniho skoku" a jako takova vnasi do programu skrytou komplexitu, ktera je maskovana za na prvni pohled jednodussi kod, ktery je ovsem roztrousen na mnohem vice mistech. "Hloupe" C-ckove osetrovani navratovych hodnot a odporna hromada if-u pro kazde volani sice neni hezka, ale je LOKALNI - vse je na jednom miste.

Ja vidim pointu v pripade vetsiho projektu, na kterem se podili vetsi mnozstvi ruzne schopnych lidi: pokud pisete v C a otrocky osetrujete kazde volani (a mohou vam to automaticky kontrolovat ruzne nastroje), je celkem trivialni prostym pohledem na cizi (!!) kod jedne funkce/metody zjistit, zda je vse osetreno a behem chvile vite, zda je kod v poradku a nebo neni. V pripade vyjimek to pohledem zjistit nelze, protoze MUSITE odskocit na implementaci kazde volane funkce/metody (je to cizi kod, ktery neznate!!) a overit si co vyhazuje/zachycuje.

Navic jak zde nekolikrat padlo, 100% dodrzovat RAII je TEZKE a kontrolovat dodrzovani RAII v teamu 10+ lidi, pri blizici se deadline je to TEMER NEMOZNE. Toto bych rad zduraznil, protoze to ilustruje cely muj problem c C++. C++ MUZE BYT relativne moderni jazyk (ehm..), bez nebezpecnych pasti a neustalych SEGFAULTu - ale vyzaduje to KAZEN A DISCIPLINU pri psani kodu.
Naproti tomu kod v C, pri dodrizovani KAZNE A DISCIPLINY (a pouziti treba gobject knihovny) muze byt relativne moderni (ehm, ehm, ehm!), bez nebezpecnych pasti a neustalych SEGFAULTu.
Tedy z tohoto pohledu jsou temer EKVIVALETNI (jediny vetsi rozdil je ze kod v c++ je tak o 20-30% kratsi). Jediny PODSTATNY rozdil je pak to, jak dobre je mozne ZARUCIT, ze vsichni clenove teamu budou dodrzovat kazen a disciplinu. A zde C++ prohrava na cele care.

Co jsem tim vsim chtel/nechtel rict: netvrdim ze vyjimky jsou "spatne", ale ze jejich pouzivani ma i svou odvracenou stranu a je dobre byt si vedom toho, ze se jedna o velmi zakerne skrytou komplexitu, ktera se maskuje kratsim a citelnejsim kodem. A TO beru jako hlavni poselstvi toho, dnes jiz ponekud zastaraleho, clanku.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 15:56:24
Programování obecně vyžaduje kázeň a zejména dodržování různých pravidel a dbát na koncepci a organizaci celého projektu. Platí pro C i pro C++. Chápu, že profesionálové dlouhé léta pracující v C se neradi přeučují na něco jiného. Protože ono se v C++ opravdu programuje úplně jinak. Ale i to vyžaduje kázeň, protože je sice spoustu věcí, které za vás C++ udělá, ale zase je třeba mít o tom přehled, rozumět tomu a vědět co dělám a proč to dělám. Když Cčkař začne kritizovat už jenom třeba fungování obyčejného destruktoru s tím, že nikdy neví přesně, co se uvnitř děje, pak ... já ho celkem chápu ... ale buď musí na sobě pořádně zapracovat, aby se v C++ naučil, a nebo ať to tedy dál matlá v tom Cčku no.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: belzebub 07. 02. 2014, 16:18:58
Programování obecně vyžaduje kázeň a zejména dodržování různých pravidel a dbát na koncepci a organizaci celého projektu. Platí pro C i pro C++. Chápu, že profesionálové dlouhé léta pracující v C se neradi přeučují na něco jiného. Protože ono se v C++ opravdu programuje úplně jinak. Ale i to vyžaduje kázeň, protože je sice spoustu věcí, které za vás C++ udělá, ale zase je třeba mít o tom přehled, rozumět tomu a vědět co dělám a proč to dělám. Když Cčkař začne kritizovat už jenom třeba fungování obyčejného destruktoru s tím, že nikdy neví přesně, co se uvnitř děje, pak ... já ho celkem chápu ... ale buď musí na sobě pořádně zapracovat, aby se v C++ naučil, a nebo ať to tedy dál matlá v tom Cčku no.

Opet jste nepochopil o cem mluvim. V teamu mate 10 lidi, z nich 2-3 jsou "dobri", 3 "prumerni" a zbytek jsou matlalove. To co pisete musi pochopit a dodrzovat jak ti "dobri", tak "matlalove". O tom mluvim.
Rad bych ignoroval Vase narazky na moji schopnost/neschopnost, protoze to nema nic spolecneho s tim o cem mluvim, ale nejsem splachovaci, a proto se musim branit: V C++ delam od r.1993, profesionalne od 1999. Naucil jsem se drive C++, nez C (ne, to neni protimluv - psat udrzovatelne a korektne velky projekt v C je neco jineho nez "nepouzivat tridy a templaty" v C++) a na C jsme presli (cely team) kolem roku 2008. Muzete mi samozrejme neverit, a myslet si, ze je mi 70 a rozciluje me ta dnesni mladez co pise same templaty a objekty, kterym jako geront nerozumim, misto aby pekne vyfiknuli pointerovou aritmetiku, na ktere baziruji protoze to je jedine co umim. Pripadne Vam nabizim jeste alternativu, ze je mi 13 let a kompletne blabolim, protoze umim pouze VisualBasic, nicmene jsem kdesi cetl, ze "v cecku" delaji profici a tak machruji.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 07. 02. 2014, 17:02:16
Fail-fast is a property of a system or module with respect to its response to failures. A fail-fast system is designed to immediately report at its interface any failure or condition that is likely to lead to failure. Fail-fast systems are usually designed to stop normal operation rather than attempt to continue a possibly flawed process.

Což je přesně to, co dělají výjimky: přeruší výpočet (fail-fast module) a skočí do handleru (next-higher system design level). Všimněte si, že se tam nikde nepíše nic o tom, že má skončit celý program. Kdyby ale měl, opět tu budou řešit výjimky lépe než SIGSEGV. Ten totiž třeba nezaloguje vzniklou chybu a neuklidí sdílené zdroje.

Netvrdim, ze je clanek bezchybny (taky je uz HODNE stary - coz ilustruje zminka o RAII jako o necem neobvyklem), ale me ve sve dobe primel hluboce se zamyslet nad vlastnim konceptem vyjimky, ktera je z principu urcita forma "nelokalniho skoku" a jako takova vnasi do programu skrytou komplexitu, ktera je maskovana za na prvni pohled jednodussi kod, ktery je ovsem roztrousen na mnohem vice mistech. "Hloupe" C-ckove osetrovani navratovych hodnot a odporna hromada if-u pro kazde volani sice neni hezka, ale je LOKALNI - vse je na jednom miste.

RAII je koncept z roku 1994 a je i v ISO C++98. Ten článek je z roku 2005.

Lokální v těch ifech je pouze úklid. A ten v C++ dělají destruktory.

Ja vidim pointu v pripade vetsiho projektu, na kterem se podili vetsi mnozstvi ruzne schopnych lidi: pokud pisete v C a otrocky osetrujete kazde volani (a mohou vam to automaticky kontrolovat ruzne nastroje), je celkem trivialni prostym pohledem na cizi (!!) kod jedne funkce/metody zjistit, zda je vse osetreno a behem chvile vite, zda je kod v poradku a nebo neni. V pripade vyjimek to pohledem zjistit nelze, protoze MUSITE odskocit na implementaci kazde volane funkce/metody (je to cizi kod, ktery neznate!!) a overit si co vyhazuje/zachycuje.

Není. Je naopak velmi obtížné najít, že mezi 15 ify jeden chybí. Nebo že má opačně podmínku. Výjimka má tu výhodu, že se o ně nemusíte starat po cestě, ale pouze tam, kde je vyhozena, a tam, kde je zachycena.

Výjimky by měly být dokumentované, stejně jako by mělo být dokumentované, jestli ta funkce vrací 0 = success (libc konvence) nebo 0 = failure (bool konvence).

Pokud výjimky neznáte, můžete je pořád chytat pomocí catch (const std::exception &e) nebo catch (...).

Navic jak zde nekolikrat padlo, 100% dodrzovat RAII je TEZKE a kontrolovat dodrzovani RAII v teamu 10+ lidi, pri blizici se deadline je to TEMER NEMOZNE. Toto bych rad zduraznil, protoze to ilustruje cely muj problem c C++. C++ MUZE BYT relativne moderni jazyk (ehm..), bez nebezpecnych pasti a neustalych SEGFAULTu - ale vyzaduje to KAZEN A DISCIPLINU pri psani kodu.
Naproti tomu kod v C, pri dodrizovani KAZNE A DISCIPLINY (a pouziti treba gobject knihovny) muze byt relativne moderni (ehm, ehm, ehm!), bez nebezpecnych pasti a neustalych SEGFAULTu.
Tedy z tohoto pohledu jsou temer EKVIVALETNI (jediny vetsi rozdil je ze kod v c++ je tak o 20-30% kratsi). Jediny PODSTATNY rozdil je pak to, jak dobre je mozne ZARUCIT, ze vsichni clenove teamu budou dodrzovat kazen a disciplinu. A zde C++ prohrava na cele care.

Možná to taky zaznělo, ale není to pravda. Dodržování RAII je naopak velmi snadné: každá alokace zdroje znamená vytvoření objektu a objekt spravující zdroje spravuje maximálně jeden zdroj a nic jiného. Problém jsou spíš programátoři „teď to napíšu takhle, opravím to později“, kteří RAII nedodrží, což je ale to samé, jako když mezi 15 ifů vloží narychlo jeden příkaz bez ifu.

Já mám přesně opačnou zkušenost, a to že raw pointery prohrávají na celé čáře.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 07. 02. 2014, 18:11:30
Mno a na zaver ...

http://c2.com/cgi/wiki?BewareOfExceptionsInTheDestructor

Citace
  • Objects with throwing destructors cannot be used in Standard containers
  • Objects with throwing destructors cannot even be composed safely
  • In the new C++11 standard all throwing destructors terminate the program, even when not UnwindingTheStack


Následně se tam pokračuje tím, že destruktory by měly dělat pouze rollback, takže nemají proč vyhazovat výjimky. Ano, to je možné paradigma, ale pro otázku házejících destruktorů to zaměňuje důvod a následek.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 23:23:10
Nic kromě sdílení dat mezi workery, jako třeba statistiky nebo nonce pro autorizaci, ale to je maličkost. On to ten Apache asi používá jenom pro parádu ::)
Apache umí statistiky i mimo sdílenou pamět (ScoreBoardFile). Nonce autorizaci nemusím používat a když to chci, tak se buď smířím s tím, že se rozhodli pro sdílenou pamět, nebo se podívám po jiném řešení (sdílení dat třeba v memcached a mám to i mezi různými instancemi serverů).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 23:28:21
Mimochodem zajímalo by mě, jak se dá s výjimkou v destruktoru vyřešit tohle:
Kód: [Vybrat]
#include <iostream>
#include <stdlib.h>

class A
{
public:
    ~A()
    {
        throw 1;
    }

    void* operator new(size_t n)
    {
        return malloc(n);
    }
   
    void operator delete(void *p)
    {
        if (p != 0) free(p);
    }
};

int main()
{
    try
    {
        A* a = new A;
        delete a;
    }
    catch (const int e)
    {
        std::cout << "exception " << e << std::endl;
    }

    return 0;
}
Ten přetížený operátor delete se totiž při vyhození výjimky vůbec nezavolá, takže to leakuje:
valgrind --leak-check=full ./a.out
==2647== HEAP SUMMARY:
==2647==     in use at exit: 1 bytes in 1 blocks
==2647==   total heap usage: 2 allocs, 1 frees, 133 bytes allocated
==2647==
==2647== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2647==    at 0x4C244E8: malloc (vg_replace_malloc.c:236)
==2647==    by 0x400B5E: main (in a.out)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: gamer 07. 02. 2014, 23:33:24
Ono se to delete dokonce nezavolá ani když není přetížené, což výjimky v destruktoru dělá úplně nepoužitelnými.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 07. 02. 2014, 23:38:27
Mimochodem zajímalo by mě, jak se dá s výjimkou v destruktoru vyřešit tohle:
Kód: [Vybrat]


Bugy jsou bohužel v každém software

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55635
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 08. 02. 2014, 00:22:35
Jak jsem říkal a vracím se k třetímu příspěvku. Chce to prostě vyzkoušet. Jedna věc je, co říká norma, druhá věc je, co implementují překladače.

GCC 4.7.1 - delete nezavolá
na codepadu je verze 4.1, ale chová se to stejně

http://codepad.org/z5NtbxMy

nezavolá ani když má virtuální destruktor

http://codepad.org/X7utHYHZ

přičemž normálně by se volat měl

http://codepad.org/HHKUxfYq

Chová se to naprosto stejně i v případě, že zapnu std=C++11

Jak je na tom Visual Studio?
Mám jen verzi 2008. V pondělí mohu zkusit 2010

Visual Studio je na tom stejně. Navíc dokonce visual studio nezavolá ani standardní delete a nechá paměť leaknout.

Smutné. Takže ani jeden z předních překladačů neimplementuje normu správně.  Zajímavý je, že o chybě se diskutovalo už v roce 2002
http://wg21.cmeerw.net/cwg/issue353
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Jakub Galgonek 08. 02. 2014, 01:29:01
Jak je na tom Visual Studio?
Mám jen verzi 2008. V pondělí mohu zkusit 2010

Intel C++ Compiler delete nezavolá, stejně tak (zdá se) Visual Studio 2010. Kde to ale funguje je clang++.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 08. 02. 2014, 10:51:33

Intel C++ Compiler delete nezavolá, stejně tak (zdá se) Visual Studio 2010. Kde to ale funguje je clang++.

Potvrzuju, clang-3.3 (Ubuntu 12.04 LTS) funguje jak má

 
Kód: [Vybrat]
ondra@cornelie:/tmp$ clang++ raw.cpp
ondra@cornelie:/tmp$ ./a.out
create
new
destroy
destruct
delete
exception 1
ondra@cornelie:/tmp$

Mimochodem, o existenci clangu jsem doposud nic nevěděl. Jdu ho vyzkoušet.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: RAII 08. 02. 2014, 13:02:13
To Ondřej Novák: hele, jak si moh používat tu svoji knihovnu která hází exceptions v destruktorech když žádný kompiler (GCC,Intel C++ a VS) tuto schopnost naprosto správně neimplementuje?
Clang prej fachčí, ale o něm si zase nevěděl. No vysvětli mi prosím ten chaos...
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 08. 02. 2014, 13:30:18
To Ondřej Novák: hele, jak si moh používat tu svoji knihovnu která hází exceptions v destruktorech když žádný kompiler (GCC,Intel C++ a VS) tuto schopnost naprosto správně neimplementuje?
Clang prej fachčí, ale o něm si zase nevěděl. No vysvětli mi prosím ten chaos...

Tohle není žádná záhada. Destruktory hází jen zřídka a to, že se v tom případě nezavolá delete znamená jen memory leak. Samozřejmě předpokládám rozumné operátory delete, ne žádné divočiny. Navíc bych u objektů s nějakým složitým destruktorem nečekal, že budou zabírat moc paměti, pokud vůbec buou na heapu. Velké bloky paměti budou spíš alokované zvlášť a spravované nějakým chytrým ukazatelem, který se v tomhle případě zruší správně.
Takže i s tímhle bugem program tu a tam leakne maximálně tak pár kilo. Na nasbírání problematického množství paměti bude třeba hódně času. Dřív přijde restart, nebo ten program zdechne na něco jiného.

Samozřejmě že dokážu vymyslet příklad, kdy to těžce selže. Třeba veliké pole objektů s házejícími destruktory.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 08. 02. 2014, 14:30:58
Což o to leaky mi kontrolují různé nástroje a nikdy jsem si žádného nevšiml. Asi zpravidla proto, že většinou jde o objekty, u kterých se předpokládá, že vzniknou na zásobníku. Má oblíbená architektura totiž vypadá takto

1.  objekt co vlastní zdroj
2.  objekt co s ním manipuluje (manipulant - builder)

Ten co vlastní zdroj, ten bývá často (ale ne vždy) alokován dynamicky. Naopak builder bývá výhradně na zásobníku. Builder většinou v destruktoru uvádí zdroj do konzistentního stavu, tak aby se případně dal používat s nějakým dalším builderem. Pokud by builder nedokázal zanechat zdroj v konzistentním stavu, tak je to fatal error a právě zde se má / měla hodit výjimka. Nedokážu si představit, že by se to zaignorovalo. Případný další builder bude pracovat s nekonzistentním zdrojem... NO WAY... to je těžkej malér.

Jediný co mě napadlo, jak by se to dalo řešit je výjimku forwardovat budoucímu builderovi, který ji výhodí v konstruktoru. Znamená to ale, abych si u zdroje vedl jeho stav, nebo ještě jednodušeji, abych výjimku zachycenou v destruktoru místo vyhození uložil do objektu držící zdroj.

Ani to neřeší problém 100%. Pokud mám pool zdrojů, třeba databázových spojení a já uložím do poolu nekonzistentní zdroj (protože jsem se nedozvěděl, že je nekonzistentní, žádná výjimka nevylítla), tak i forwardovaná výjimka vylítne tomu, kdo další takový zdroj vytáhne z poolu. A ohlásí chybu  ... to může být třeba další request na serveru... chyba se ohlásí tedy někomu, kdo za to nemůže... NO WAY!

Takže mě ještě napadl další workaround. Dodělat do všech možných zdrojů, které mám, kromě funkce forwardování výjimky, taky funkci na kontrolu integrity (checkIntegrity()). Tato funkce by mohla hodit forwardovanou výjimku. Před uložením do poolu by se to zavolalo a pokud by výjimka vylítla, tak by se zdroj rovnou zlikvidoval a do poolu by se nevracel. Bohužel, vracení do poolu je realizováno v destruktoru  :D Takže onu výjimku si mohu maximálně strčit ... někam do logu.

Ještě příklad. Když mi v destruktoru transakce falíruje operace rollback, už nemám šanci se to dozvědět (a to ani v případě, že neletí žádná výjimka) a maximálně při vracení mysql spojení do poolu mohu ověřit, jestli tam není někde forwardovaná výjimka a pokud ano, tak ji zalogovat a z databáze se odpojit ... snižit počet aktivních spojení z databází, pool pak vyrobí novou konekci.

Uff a to je kvůli tomu, že mi selhal rollback
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: ferren 08. 02. 2014, 15:38:12
me tato cela debata prijde ponekud mimo. nejak jsem to tu proskakal, a v podstate jsou tu 2 tabory a oba mi prijdou ponekud mimo.
kvizova otazka, z diskutujicich podilel se nekdy nekdo na programu,ktery MUSEL neustale bezet? treba z pouheho duvodu ze minutovy vypadek znamena obrovskou ztratu penez? jsem se ve sve kariere navstivil 2 pomerne nesourode svety. jeden zalozeny ciste na vykon (hpc) a druhy naopak slow computing, ale pozadavek na nonstop beh bez jedine minuty vypadku (telco).

vyjimky jsou zlo, a co vim se nic takoveho nikde nepouziva. vetsina spolecnosti v high availability by si nic takoveho neodovolila. je treba dusledne osetrovat navratove stavy, psat vse dusledne tak aby vubec k zacdnemu stavu,ktery by se vubec dal osetrovat exceptiony ani nemohlo dojit. tady to nekolikrat omilane fopen bez toho mizerneho IFu, osetrovat tu pres vyjimku je vylozene napresdrzku. taky ne mnohdy slusnejsi napresto regulerne crashnout az na zem, ale ani se nepokusit znekonzistenit nejaka data. koneckoncu jakekoli high availability reseni pocita s behem vice instanci master/slave rizenych opet nekolikrat jistenym directorem, takze crash/restart je to mensi zlo nez hejrup osetrovani exceptionu v odflaklych kodech...
a ten zde zminovany autopilot, presne tak se to dela...autopilotu bezi vzdy paralelne vic, jsou kontrolovany zvenku. krom toho ze jsou temer jiste psane v jazycich co tyhle zparchantele exception handlingy ani neumoznuji, tak jiste je v nich daleko vetsi pruser kdyz vubec dojde ke stavu,ktery je nutne nejak osetrovat a pokud, tyhle systemy se zcela regulerne a spravne cele restartuji, protoze to je v tomto pripade jedine konzistentni reseni....
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 08. 02. 2014, 15:58:06


Tenhle příspěvek je zbytečný. Ani oddělení autopilotů na samostatné počítače a jejich restart (restart celých počítačů v době chyby) neznamená, že to vždy bude konzistentní. Už jsem viděl dost chyb založených na tom, že nějaký řídící program zanechal svěřený prostředek v nekonzistentním stavu a po restartu nebyl schopen práce. Bylo nutné jít a manuálně uvést ten prostředek do počátečního stavu, aby se řídící program chytil ... byl to nějaký souřadnicový řezací stroj ... ano, měl i standardní inicializaci, ale ono to bylo složitější.

Od té doby mám rád výjimky, protože je tu pořád slušná šance, že při chybě budou k dispozici data potřebná k nápravě. Když program spadně sigsegv, nelze o jeho stavi nic říct. Když je od někud vyhozená výjimka, jedná se o řízený pád, kde každý komponenta vzniklá během činnosti dostane informaci o tom, že se něco děje a může na to vhodně reagovat. Process je to řízený, takže není nebezpečný, naopak nebezpečné je to nechat spadnou. A neberu ani argument, že to takhle dělají nějaké přetechnizované firmy s vysokým stupněm zabezpečení. Z téhle diskuze je vidět, že výjimkám ještě hodně lidí nerozumí a tak je pro zodpovědné osoby mnohem snažší a bezpečnější spolehnout se ještě na jiné technologie.

Nehledě na oblíbený problém zakonzervovaných seniorů, kteří se těší důvěře produktových managerů a jinak mají na celý problém přesně ten názor, jaký zde prezentujete, aniž by bylo podpořen jediným argumentem

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: majo33 08. 02. 2014, 16:27:16
Opet jste nepochopil o cem mluvim. V teamu mate 10 lidi, z nich 2-3 jsou "dobri", 3 "prumerni" a zbytek jsou matlalove. To co pisete musi pochopit a dodrzovat jak ti "dobri", tak "matlalove". O tom mluvim.
Rad bych ignoroval Vase narazky na moji schopnost/neschopnost, protoze to nema nic spolecneho s tim o cem mluvim, ale nejsem splachovaci, a proto se musim branit: V C++ delam od r.1993, profesionalne od 1999. Naucil jsem se drive C++, nez C (ne, to neni protimluv - psat udrzovatelne a korektne velky projekt v C je neco jineho nez "nepouzivat tridy a templaty" v C++) a na C jsme presli (cely team) kolem roku 2008. Muzete mi samozrejme neverit, a myslet si, ze je mi 70 a rozciluje me ta dnesni mladez co pise same templaty a objekty, kterym jako geront nerozumim, misto aby pekne vyfiknuli pointerovou aritmetiku, na ktere baziruji protoze to je jedine co umim. Pripadne Vam nabizim jeste alternativu, ze je mi 13 let a kompletne blabolim, protoze umim pouze VisualBasic, nicmene jsem kdesi cetl, ze "v cecku" delaji profici a tak machruji.

Zaujima prechod z C++ na C. Kedze s tym mas skusenosti prosim Ta mohol by si to trochu viac rozviest. Napr. aky velky projekt robite? Ako mate implementovane kontajnery (makrami, ...)?  Pouzivate vela makier? Nechyba vam obcas C++? Rychlost kompilacie je asi podstatne vyssia? Pouzivate GUI? Nemuseli ste si vyrobit objektovy system podobny GObject-u? Pouzivate GOTO (napr. ako nahrada vynimiek)? Kludne mi to mozes poslat ako PM, aby sme nespamovali toto vlako.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: ferren 08. 02. 2014, 16:29:02
co si predstavujete pod tim ze "zachoval prostredek v nekonzistentnim stavu" ?  napr ulozena data? ty jsou nekonzistentni z 2 duvodu 1) chyba v algoritmu co data generuje b) selhani zapisu jako takoveho( vetsinou hw problem) . tohle je mozne, ale to je zcela mimo sferu toho zda exceptiony ano/ne protoze ani jeden problem tohle neresi.
exceptiony vedou k lajdackemu psani kodu, aneb proc osetrovat kazdou prkotinu kdyz to muzem vzit po blocich....zavadeji sposutu moznosti jak si usnadnit zivot a nepremyslet o tom co proc takto pisu. (samozrejme to nevypliva z zavedeni exc handlingu jako takoveho, ale je to logicky dusledek pro prirozene line lidi)

btw mozna i casove by bylo jednodusi napsat lepe ten ohandlovany kod nez se drbat pravou rukou na .... a rozjimat o vyjimkach v destruktorech na akademicke urovni....

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: ferren 08. 02. 2014, 16:45:30
majo33:
prechod z c++ na c je mozna az ponekud extremni reseni,ale neni problem psat kod v c++ 11 a presto aby to na pohled vypadalo pomejne "cckoidne" a dalo se takovy kod nejen rychle psat, ale i cist (coz je to co c++ neustale ztraci a dohani z pohledu citelnosti i takove proslavene bastlu jako perl ;-)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 08. 02. 2014, 17:35:58
btw mozna i casove by bylo jednodusi napsat lepe ten ohandlovany kod nez se drbat pravou rukou na .... a rozjimat o vyjimkach v destruktorech na akademicke urovni....

Vůbec netušíš o čem mluvíš. Kód, který tady řeším vypadá takto:

Kód: [Vybrat]
{
Connection conn = mysqlpool.get();
Transaction trn(conn);
Result res = trn.SELECT("*").FROM(...).....;
....
trn.commit();
}

Z každého příkazu může vylítnou výjimka.
Z mysqlpool.get() například když dojdou spojení v poolu a vyprší timeout
Z transaction v okamžiku, kdy databáze odmítne příkaz BEGIN
Z sql příkazu může vylítnout cokoliv, třeba když napíšu ten dotaz blbě, nebo když databáze mezitím spadne
Z commitu může vypadnout taky víceméně cokoliv, pokud to databáze odmítne commitonouy

Víceméně všechny výjimky odchytává rpc server, ihned po návratu z handleru a vrací na to chybu 500. Zároveň do logu zapisuje chybovou hlášku, která vylítla s výjimkou.

Dám ti úkol. Napiš to bez výjimek. A zkus to navrhnout tak, aby se takový kód dal použít třeba 234x (zhruba tolik metod má můj server na svém RPC rozhraní) - poznámka pro rejpaly, ono to tělo není vždy úplně stejné a některé metody tedy databázi nepotřebují, ale moc jich není.

Ta akademická diskuze je jen o tom, jestli mají létat výjimky z té časti mezi posledním příkazem a složenou závorkou (třeba při rollbacku transakce, nebo chyba při vracení spojení do poolu.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: ferren 08. 02. 2014, 18:40:57
ad ten priklad, db nejsou vubec ma parketa, takze kdyz koukam jen na ty radky aniz bych vedel co uvnitr delaji tak strelba od boku...

1) Connection conn = mysqlpool.get();
hm normalne by clovek ocekaval ze pool cehokoli me spis vrati adresu a ne hodnotu ale budiz treba to tak je netusim jak je ta tva Conection deklarovana pripadne velka/slozita/konstruovana...

2) Transaction trn(conn);
kdyz odmyslime trivialni variantu na if(conn.valid()) ktera dela obrovsky ifovy overhead kolem tak se to da rozumne poresit uvnitr toho konstruktoru aby na nevadilni connection vznikla reguleni nevalidni transaction co na jakykoli nasledujici command jako...
Result res = trn.SELECT("*").FROM(...).....;
proste nic taky neudela, to plati i na ten nasledujici commit()

kdyz to bude reseno uvnitr,navenek ten kod muze byt uplne identicky jak ten tvuj, zadny if-ovy balast kolem a presto nemusi nikdy generovat vyjimky. konkretni realizace je spis zavisla na cetnosti/pravdepodobnosti nevalidnich stavu z hlediska performance....

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Jakub Galgonek 08. 02. 2014, 20:46:42
1) Connection conn = mysqlpool.get();
hm normalne by clovek ocekaval ze pool cehokoli me spis vrati adresu a ne hodnotu ale budiz treba to tak je netusim jak je ta tva Conection deklarovana pripadne velka/slozita/konstruovana...

Tenhle způsob může být právě docela fajn. Ta instance se mu vytvoří pěkně přímo na zásobníku, takže ušetří za volání new/delete. Navíc s opuštěním bloku se ten objekt může i automaticky sám uklidit (vrátit spojení do poolu).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 08. 02. 2014, 22:12:03
2) Transaction trn(conn);
kdyz odmyslime trivialni variantu na if(conn.valid()) ktera dela obrovsky ifovy overhead kolem tak se to da rozumne poresit uvnitr toho konstruktoru aby na nevadilni connection vznikla reguleni nevalidni transaction co na jakykoli nasledujici command jako...
Result res = trn.SELECT("*").FROM(...).....;
proste nic taky neudela, to plati i na ten nasledujici commit()

kdyz to bude reseno uvnitr,navenek ten kod muze byt uplne identicky jak ten tvuj, zadny if-ovy balast kolem a presto nemusi nikdy generovat vyjimky. konkretni realizace je spis zavisla na cetnosti/pravdepodobnosti nevalidnich stavu z hlediska performance....
Tohle snad není myšleno vážně, že ne. A co ten selekt jako vrátí za data, když bude ta transakce nevalidní? Prázdnou tabulku? Co když tam bude SELECT NOW(); který evidentně prázdnou tabulku nemůže vrátit. Co když tam bude nějaký super extra dlouhý a komplikovaný výpočet, který navíc bude záviset na datech v té databázi, takže při prázdných tabulkách bude trvat neuvěřitelně dlouho, nebo se dostane do nekonečné smyčky? Třeba proto, že to mělo něco do databáze zapsat a následně vyzvednout něco jiného, co tam nebude, takže se to bude do nekonečna opakovat? Člověče, že vás tady vůbec školím. Doporučuju se od programování držet v uctihodné vzdálenosti, a zabývat se třeba ... já nevím, tvorbou webovek v PHPku....

To jsem ještě neviděl! Jo studenti v prvním ročníku mají takové děsivé nápady.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 08. 02. 2014, 22:15:26
1) Connection conn = mysqlpool.get();
hm normalne by clovek ocekaval ze pool cehokoli me spis vrati adresu a ne hodnotu ale budiz treba to tak je netusim jak je ta tva Conection deklarovana pripadne velka/slozita/konstruovana...

Tenhle způsob může být právě docela fajn. Ta instance se mu vytvoří pěkně přímo na zásobníku, takže ušetří za volání new/delete. Navíc s opuštěním bloku se ten objekt může i automaticky sám uklidit (vrátit spojení do poolu).

On je tam vlastně ukazatel na mysql connection a destruktor, co vypůjčenou konekci vrací do nějakého kontejneru a samozřejmě ukazatel mysqlpool, aby to vědělo, kam to má vrátit. Samo se předpokládá, že v době destrukce ten mysqlpool bude ještě existovat, ale chyby tohoto druhu se stávají výjimečně.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: ferren 08. 02. 2014, 22:24:00

Tohle snad není myšleno vážně, že ne. A co ten selekt jako vrátí za data, když bude ta transakce nevalidní? Prázdnou tabulku? Co když tam bude SELECT NOW(); který evidentně prázdnou tabulku nemůže vrátit. Co když tam bude nějaký super extra dlouhý a komplikovaný výpočet, který navíc bude záviset na datech v té databázi, takže při prázdných tabulkách bude trvat neuvěřitelně dlouho, nebo se dostane do nekonečné smyčky? Třeba proto, že to mělo něco do databáze zapsat a následně vyzvednout něco jiného, co tam nebude, takže se to bude do nekonečna opakovat? Člověče, že vás tady vůbec školím. Doporučuju se od programování držet v uctihodné vzdálenosti, a zabývat se třeba ... já nevím, tvorbou webovek v PHPku....

To jsem ještě neviděl! Jo studenti v prvním ročníku mají takové děsivé nápady.
[/quote]


:-) bud jsi to nepochopil a nebo um,yslne ignoroval, samozrejme ze krom toho ze na neplatne transakci nebudes delat zadne operace,tak se zachovas scela korektne a zpusobne, tj krom toho ze to zajiste nekam zalogujes tak se zachovas tak abys neco nenarusil, nastavis nebo vrasis prislusnou chybu.
ale takova prkotina,ze namas prave volno v poolu, je zcela regulerni vec se kterou se jiste musi vsude pocitat a urcite si nezaslouzi obsluhu vyjimkou....je zcela normalni ze vetsina netrivialnich akci muze selhat

btw nevim koho chces ucit, nebudes nahodou nejaky doktorand/asistent?:-)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: ferren 08. 02. 2014, 22:27:04
1) Connection conn = mysqlpool.get();
hm normalne by clovek ocekaval ze pool cehokoli me spis vrati adresu a ne hodnotu ale budiz treba to tak je netusim jak je ta tva Conection deklarovana pripadne velka/slozita/konstruovana...

Tenhle způsob může být právě docela fajn. Ta instance se mu vytvoří pěkně přímo na zásobníku, takže ušetří za volání new/delete. Navíc s opuštěním bloku se ten objekt může i automaticky sám uklidit (vrátit spojení do poolu).

to ze se na poolove operaci nebude provadet zadne dynamicke new/delete beru za samozrejmost, k tomu vetsinou slouzi ty ruzne pametove ci kontextove pooly:-)
ono je taky fajn,pokud chces at nejaky program bezi dele nez tyden, tak se celkove vyvarovat jakyhkoli dynamickych alokaci,pokud je to principialne mozne....
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: ferren 08. 02. 2014, 22:48:04
jo a jeste neco, vetsinou se nepisou funkce/metody co vraci nejake navratove hodnoty pro srandu kralikum,vetsinou se pisi proto aby je nekdo kontroloval. da se tim vse docela jemne skalovat, od warningu pres error po fatal. resit vse humpolacky, s tim ze to zkusim a budu chytat vyjimky je desne nasilne a casto to sklouzava do generovani nic nerikajicich "internich chyb" protoze kdo by se pak pri handlovani vyjimky obtezoval dohledavat co konkrente selhalo a jaky byl duvod,ze?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ivorne 09. 02. 2014, 15:10:46
Takže jaký je závěr? Výjímka v destruktoru působí u většiny implementací memory leak, takže nepoužívat?
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: RAII 09. 02. 2014, 15:24:20
Ne, exceptions v destruktorech sou nechutná prasárna. Nejde tady jen o ten memory leak, destruktory prostě nesmí NIKDY selhat (taky v nich nemá co selhat -> pokud teda nevymyslíš ňákou uber kravinu). PROTOŽE destruktor ti uvolňuje prostředky objektu (paměť, zámky, soubory, streamy). Někteří hold serou na RAII a implementujou si to po svim...
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 09. 02. 2014, 15:47:47
Takže jaký je závěr? Výjímka v destruktoru působí u většiny implementací memory leak, takže nepoužívat?
Jednoduché odpovědi se tady bohužel nedočkáš. Závisí to na spoustě věcí.

Chceš vůbec používat výjimky? Ferren tu napsal dost příkladů, kdy se úplně nehodí.
Jak často budou lítat? Častější chyby je lepší řešit návratovými hodnotami. Chytat výjimky např. při každém parsování intu je nechutný opruz.
Budou ty házející objekty na haldě, nebo jenom na zásobníku? Pokud na zásobníku, tak se ten bug v delete neprojeví.
Vadí, že program výjimečně padne na hubu (std::terminate)? Jestli ne, tak klidně házej. Restart programu může být někdy nejjednodušší zotavení z chyby.
Vadí, že destruktor ve vzácných případech tu výjimku stejně nevyhodí (std::uncaught_exception)? Pokud jo, tak ten kód stejne v destruktoru být nemůže.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ivorne 09. 02. 2014, 16:34:52
Tak já si stejně myslím, že výjimky jsou dobré akorát pro rapid develpoment a prototypování. Ale pokud by se člověk rozhodl používat výjimky, tak stejně je nejlepší říct si, že destruktor je házet nesmí. Když házíš výjimky v destruktoru, tak podle této diskuse: 1) nemůžeš používat stl, 2) nesmíš tyto třídy alokovat dynamicky a 3) stejně nemáš jistotu, že to bude fungovat (std:uncaught_exception).

Ta 1) a 2) se dají přežít, ale jestli to není příliš velká cena za něco, co bude člověk používat jen zřídka a to u věcí, které se stejně dají řešit jinak. Celkem rozumné řešení mi přijde to jak tu kdosi zmiňoval v kontextu flushování output bufferu - v destruktoru to chybu potlačí (maximálně zaloguje) a pokud chce programátor zvenku tu chybu sledovat, tak před destrukcí zavolá metodu flush(). To navíc umožní řešení problému i bez smazání nebo invalidace objektu.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 09. 02. 2014, 16:35:13
Kód: [Vybrat]
{
Connection conn = mysqlpool.get();
Transaction trn(conn);
Result res = trn.SELECT("*").FROM(...).....;
....
trn.commit();
}
Tohle je dobrý příklad kódu na rozbor, jestli je z těch destruktorů vůbec třeba házet. Zrovna tady to IMO není vůbec třeba. Proč?

~Transaction zavolá rollback. Nejspíš pouze v případě, že poslední metoda nebyl úspěšný commit. Může házet třeba, pokud vypadne spojení s databází (jak mi tady O.N. před časem napsal). Rozumné databáze v takovém případě udělají rollback samy. Takže destruktor vlastně udělal přesně to, co měl, ale stejně vyletí výjimka. Já bych tu chybu teda hlásil až ve chvíli, kdy by uživatel té knihovny to spojení chtěl použít znova. Pokud nechce, tak vypadlé spojení ničemu nevadí.

~Connection vrací spojení do poolu. Na samotném vracení objektu do poolu není co vyhodit. To je nějaký ten inkrement a přešoupnutí objektu mezi spojovými seznamy. Pokud se nepodaří zamknout mutex, nebo je ten pool nějak rozbitý, tak je to stejně fatální chyba pro celý proces. Může se házet třeba když to spojení nepůjde v budoucnu znova použít. Otázka je, proč by uživatele toho poolu měla tahle chyba zajímat. Pokud je spojení špatné, tak ho pool zavře a až bude potřeba, tak ho zkusí otevřít znova. Než bude znova potřeba, tak ta síť třeba zase naskočí.

Nemá cenu házet při jakékoliv chybě. Jen při takové, která zajímá uživatele. Uživatelem tady myslím i kus kódu, který používá ty objekty.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: belzebub 09. 02. 2014, 16:53:33
...
splachovaci, a proto se musim branit: V C++ delam od r.1993, profesionalne od 1999. Naucil jsem se drive C++, nez C (ne, to neni protimluv - psat udrzovatelne a korektne velky projekt v C je neco jineho nez "nepouzivat tridy a templaty" v C++) a na C jsme presli (cely team) kolem roku 2008.
...

Zaujima prechod z C++ na C. Kedze s tym mas skusenosti prosim Ta mohol by si to trochu viac rozviest. Napr. aky velky projekt robite? Ako mate implementovane kontajnery (makrami, ...)?  Pouzivate vela makier? Nechyba vam obcas C++? Rychlost kompilacie je asi podstatne vyssia? Pouzivate GUI? Nemuseli ste si vyrobit objektovy system podobny GObject-u? Pouzivate GOTO (napr. ako nahrada vynimiek)? Kludne mi to mozes poslat ako PM, aby sme nespamovali toto vlako.

Jedna se o projekt toho typu o kterem se zminil "ferren" - neustale bezici system, kde neni vyzadovana ani tak nejvyssi rychlost, ale hlavne spolehlivost. Takze implementujeme presne to o cem "ferren" psal - jakakoliv chyba, ktera je "neopravitelna" (nejde forknout, nejde alokovat pamet, atd.) zpusobi spadnuti celeho jednoho procesu. Cela "architektura" je realizovana jednim procesem ktery spousti dalsi procesy, ktere spolu komunikuji pres local-unix-sockety. Nikde zadna sdilena pamet a zadne thready (byly, ale bylo s nimi vic problemu nez je zdravo - procesy + messages jsou sice "mene uzivatelsky pritulne", ale zato radove robustnejsi.
System je duplikovany, takze v pripade padu prebira praci jiny server, dokud nekdo neda puvodni do poradku.
Posledni verze napr. na vetsine produkcnich serveru bezi bez restartu dele nez rok.
Projekt neni ani tak velky co se tyce mnozstvi kodu, jako spise komponent - mame cca 40% kodu v C, 50% v pythonu, zbytek v plsql + nejake shell skripty a dokonce je nekde i nejaky legacy perl (nemluvim o GUI).
Tj. co se GUI tyce - ne, zadne gui, je to ciste "back-end", GUI dela jiny team.
Kontejnery - pouzivame GLib/GObject knihovny tam kde je potreba neco "trochu vysokourovnovejsiho".
Makra - pokud pominu ze GLib/GObject sam pouziva maker hodne, tak i my nejake pouzivame, ovsem ve vetsine pripadu se jedna o "lokalni" makra - pouzite v jednom souboru a nebo pro ruzna "DEBUG-mode zavisla" makra.
Co se "chybeni C++" tyce,  GObject (teoreticky) poskytuje dokonce i vic "objektovych" vlastnosti nez C++, ovsem za cenu neuveritelneho mnozstvi dosti oskliveho "boiler-plate" kodu. Vetsinu "vysokourovnovejsich" objektu tedy resime tak, ze je napiseme ve Vale (mozna si nekdo vzpomene ze jsem zde pred par dny pred valou varoval), ta je "zkompiluje" do C a pak pokracujeme dale upravami vygenerovanych souboru.
Pokud neni nutne pouzivat nejake "objektove" vlastnosti (napr.dedicnost) a staci neco jako "zapouzdreny objekt", pouzivame konstrukci s "opaque" struct, tj. v .h souboru je definovany jen "typedef struct SomeObject_ SomeObject;" a funkce pracujici s "SomeObject" - tim mame vzdy dvojici ".h" a ".c" souboru fungujicich jako urcita "zapouzdrena jednotka". To je asi hlavni bod "prechodu na C" a zaroven vyreseni problemu neexistence "namespace" v C - vetsina "discipliny" je o tom dodrzet urcita pravidla pro jmena funkci a nektere "povinne" funkce/makra (new, delete, cast, asserty)
Tj. pouzivame vedle sebe jak GObject objekty (odlisene prefixem) i "dumb" objekty (viz vyse). Rekl bych ze GObject objektu mame tak 5% a postupne je eliminujeme ("vyssi" funkcnost se presouva do pythonu a plsql).
GOTO pouzivame velmi silne, ovsem z 99% pouze pri osetrovani chyb pri "komplikovanejsi" konstrukci objektu k zaruceni pouze jednoho return-u a/nebo k zajisteni korektniho "uklidu" pri chybe v konstrukci objektu. Takze se da rict, ze v ramci jedne funkce GOTO do urcite miry supluje vyjimky.
Jinak na to co by mely byt vyjimky pouzivame GError.
Atd, atd.
Co se zkusenosti tyce - nejvetsi plus je spolehlivost - za posledni 3 roky jsme meli pouze jeden "zahadny bug", ktery se navic nakonec ukazal jako spatne nastaveni objektu z externi knihovny. Obecne muzu rici, ze debugovani Cckoveho kodu, i kdyz je ho vice, je mnohem snazsi, diky tomu, ze C-cko je primitivnejsi, tak je i vetsina chyb "jednodussich" - paradoxne jsme jeste nemeli memory leak nebo segfault v produkci - coz s C++ bylo celkem caste. Ja osobne to pricitam efektu, ktery jsem mockrat v ruznych variantach cetl na ruznych mistech - protoze debugovani je slozitejsi nez psani kodu, tak kdyz napisu kod tak chytry jak to jen dokazu, tak ho pak nedokazu debugovat.
Navic code-review je opet mnohem jednodussi, protoze neni nutne zjistovat zadne vnorene vyjimky, zadne pretizene operatory, a hlavne, kdo nekdy videl jak vypada hlaseni chyby kompilatoru z nekolikrat "zanoreneho" templatu, tak oceni ze temer kazda chyba je celkem zjevna.
Nekteri lide vyzdvihovali jako prednost veci jako: stabilni ABI (u C++ to je trochu horsi), dostupnost C kompilatoru pro snad kazdou existujici platformu (vcetne embedded) - coz myslim uz dnes neni moc pravda (a mozna to nebyla pravda ani v 2008).
Rychlost kompilace rozhodne JE plus - kompilace celeho C kodu je na mem notebooku pod 30 sekund - tehdejsi C++ varianta se kompilovala i pres 10 minut (ano, mel jsem pomalejsi pocitac) - ovsem diky modularite projektu to neni az takove plus jak by se mohlo zdat.

Abych jen nechvalil - jak jiz jsem zminil, hlavni nevyhodou je delsi kod a slozitejsi testovani (mame vicemene 99% kodu pokryto unittesty), kde je nutne pouzit trochu jine principy (klasicke mockobjekty definujici nejaky interface jdou s GObject pouzit, ale je to vesmes vice prace nez se vyplati). Rozhodne je nevyhoda i to, ze kazdeho noveho programatora je nutne "prevychovat", protoze kdyz nekdo napise v CV, ze "umi C", mysli tim v 99% ze umi C++ a tedy ze vlastne umi i C.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Jakub Galgonek 09. 02. 2014, 17:22:13
Tohle je dobrý příklad kódu na rozbor, jestli je z těch destruktorů vůbec třeba házet. Zrovna tady to IMO není vůbec třeba. Proč?

Tohle ale nebyl vůbec příklad na to, jestli je třeba z destruktorů házet výjimky  :)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 09. 02. 2014, 17:25:13
Tohle je dobrý příklad kódu na rozbor, jestli je z těch destruktorů vůbec třeba házet. Zrovna tady to IMO není vůbec třeba. Proč?

Tohle ale nebyl vůbec příklad na to, jestli je třeba z destruktorů házet výjimky  :)

To ne, ale o pár stránek dřív dal Ondřej Novák právě ten objekt Transakce jako příklad, kdy se hodí ty výjimky z destruktoru házet. Tak jsem si řekl, že ten kód rozeberu i z tohohle pohledu.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: randolf 10. 02. 2014, 14:00:49
...
splachovaci, a proto se musim branit: V C++ delam od r.1993, profesionalne od 1999. Naucil jsem se drive C++, nez C (ne, to neni protimluv - psat udrzovatelne a korektne velky projekt v C je neco jineho nez "nepouzivat tridy a templaty" v C++) a na C jsme presli (cely team) kolem roku 2008.
...

Zaujima prechod z C++ na C. Kedze s tym mas skusenosti prosim Ta mohol by si to trochu viac rozviest. Napr. aky velky projekt robite? Ako mate implementovane kontajnery (makrami, ...)?  Pouzivate vela makier? Nechyba vam obcas C++? Rychlost kompilacie je asi podstatne vyssia? Pouzivate GUI? Nemuseli ste si vyrobit objektovy system podobny GObject-u? Pouzivate GOTO (napr. ako nahrada vynimiek)? Kludne mi to mozes poslat ako PM, aby sme nespamovali toto vlako.

Jedna se o projekt toho typu o kterem se zminil "ferren" - neustale bezici system, kde neni vyzadovana ani tak nejvyssi rychlost, ale hlavne spolehlivost. Takze implementujeme presne to o cem "ferren" psal - jakakoliv chyba, ktera je "neopravitelna" (nejde forknout, nejde alokovat pamet, atd.) zpusobi spadnuti celeho jednoho procesu. Cela "architektura" je realizovana jednim procesem ktery spousti dalsi procesy, ktere spolu komunikuji pres local-unix-sockety. Nikde zadna sdilena pamet a zadne thready (byly, ale bylo s nimi vic problemu nez je zdravo - procesy + messages jsou sice "mene uzivatelsky pritulne", ale zato radove robustnejsi.
System je duplikovany, takze v pripade padu prebira praci jiny server, dokud nekdo neda puvodni do poradku.
Posledni verze napr. na vetsine produkcnich serveru bezi bez restartu dele nez rok.
Projekt neni ani tak velky co se tyce mnozstvi kodu, jako spise komponent - mame cca 40% kodu v C, 50% v pythonu, zbytek v plsql + nejake shell skripty a dokonce je nekde i nejaky legacy perl (nemluvim o GUI).
Tj. co se GUI tyce - ne, zadne gui, je to ciste "back-end", GUI dela jiny team.
Kontejnery - pouzivame GLib/GObject knihovny tam kde je potreba neco "trochu vysokourovnovejsiho".
Makra - pokud pominu ze GLib/GObject sam pouziva maker hodne, tak i my nejake pouzivame, ovsem ve vetsine pripadu se jedna o "lokalni" makra - pouzite v jednom souboru a nebo pro ruzna "DEBUG-mode zavisla" makra.
Co se "chybeni C++" tyce,  GObject (teoreticky) poskytuje dokonce i vic "objektovych" vlastnosti nez C++, ovsem za cenu neuveritelneho mnozstvi dosti oskliveho "boiler-plate" kodu. Vetsinu "vysokourovnovejsich" objektu tedy resime tak, ze je napiseme ve Vale (mozna si nekdo vzpomene ze jsem zde pred par dny pred valou varoval), ta je "zkompiluje" do C a pak pokracujeme dale upravami vygenerovanych souboru.
Pokud neni nutne pouzivat nejake "objektove" vlastnosti (napr.dedicnost) a staci neco jako "zapouzdreny objekt", pouzivame konstrukci s "opaque" struct, tj. v .h souboru je definovany jen "typedef struct SomeObject_ SomeObject;" a funkce pracujici s "SomeObject" - tim mame vzdy dvojici ".h" a ".c" souboru fungujicich jako urcita "zapouzdrena jednotka". To je asi hlavni bod "prechodu na C" a zaroven vyreseni problemu neexistence "namespace" v C - vetsina "discipliny" je o tom dodrzet urcita pravidla pro jmena funkci a nektere "povinne" funkce/makra (new, delete, cast, asserty)
Tj. pouzivame vedle sebe jak GObject objekty (odlisene prefixem) i "dumb" objekty (viz vyse). Rekl bych ze GObject objektu mame tak 5% a postupne je eliminujeme ("vyssi" funkcnost se presouva do pythonu a plsql).
GOTO pouzivame velmi silne, ovsem z 99% pouze pri osetrovani chyb pri "komplikovanejsi" konstrukci objektu k zaruceni pouze jednoho return-u a/nebo k zajisteni korektniho "uklidu" pri chybe v konstrukci objektu. Takze se da rict, ze v ramci jedne funkce GOTO do urcite miry supluje vyjimky.
Jinak na to co by mely byt vyjimky pouzivame GError.
Atd, atd.
Co se zkusenosti tyce - nejvetsi plus je spolehlivost - za posledni 3 roky jsme meli pouze jeden "zahadny bug", ktery se navic nakonec ukazal jako spatne nastaveni objektu z externi knihovny. Obecne muzu rici, ze debugovani Cckoveho kodu, i kdyz je ho vice, je mnohem snazsi, diky tomu, ze C-cko je primitivnejsi, tak je i vetsina chyb "jednodussich" - paradoxne jsme jeste nemeli memory leak nebo segfault v produkci - coz s C++ bylo celkem caste. Ja osobne to pricitam efektu, ktery jsem mockrat v ruznych variantach cetl na ruznych mistech - protoze debugovani je slozitejsi nez psani kodu, tak kdyz napisu kod tak chytry jak to jen dokazu, tak ho pak nedokazu debugovat.
Navic code-review je opet mnohem jednodussi, protoze neni nutne zjistovat zadne vnorene vyjimky, zadne pretizene operatory, a hlavne, kdo nekdy videl jak vypada hlaseni chyby kompilatoru z nekolikrat "zanoreneho" templatu, tak oceni ze temer kazda chyba je celkem zjevna.
Nekteri lide vyzdvihovali jako prednost veci jako: stabilni ABI (u C++ to je trochu horsi), dostupnost C kompilatoru pro snad kazdou existujici platformu (vcetne embedded) - coz myslim uz dnes neni moc pravda (a mozna to nebyla pravda ani v 2008).
Rychlost kompilace rozhodne JE plus - kompilace celeho C kodu je na mem notebooku pod 30 sekund - tehdejsi C++ varianta se kompilovala i pres 10 minut (ano, mel jsem pomalejsi pocitac) - ovsem diky modularite projektu to neni az takove plus jak by se mohlo zdat.

Abych jen nechvalil - jak jiz jsem zminil, hlavni nevyhodou je delsi kod a slozitejsi testovani (mame vicemene 99% kodu pokryto unittesty), kde je nutne pouzit trochu jine principy (klasicke mockobjekty definujici nejaky interface jdou s GObject pouzit, ale je to vesmes vice prace nez se vyplati). Rozhodne je nevyhoda i to, ze kazdeho noveho programatora je nutne "prevychovat", protoze kdyz nekdo napise v CV, ze "umi C", mysli tim v 99% ze umi C++ a tedy ze vlastne umi i C.

Zajimave videt, jak se "high availability" da pojmout ruzne. U nas to znamena nasledujici:
- Zadna dynamicka alokace pameti. Nikdy.
- Procesy bezi, pusteny od startu/restartu RT schedulerem. Nevolaji se, pouze si predavaji data.
- Procesy jsou casove i pametove izolovane: kazdy bezi jen kdyz mu scheduler vyhradi okno a ma pristup pouze do sve pameti (jo, SHM se pouziva, ale explicitne pouze pro predavani nekterych informaci)
- Veskere myslitelne stavy systemu = normalni beh. Tedy kdyz neco nefunguje, jedna se pouze a jen o prechod na jiny stav stavove masiny, ktery se podle toho chova. Z toho jaksi automaticky vyplyva, ze zadne vyjimky. Pokud potrebuju vyjimku, tak to znamena, ze jsem nejaky stav neosetril explicitne. Stejnym zpusobem se resi i teoreticky bit-flip v registrech (prastil do nej neutron) - proces se muze pokazit --> scheduler ho restartuje, vysledek se nevezme v potaz diky redundanci.
- Implementace tech fakt kritickych veci je redundantni na ruznych architekturach, pomoci jinych nastroju a implementuji to ruzne firmy dle stejnych specifikaci --> uvnitr to jede komplet jinak, ale vysledky musi byt konzistentni. Kontrolu konzistence provadi nasledny konzument vysledku (dalsi krabice v retezu), vetsinou pomoci primitivni diskretni logiky.
- Stejne to vsechno ma dalsi zalohy, i kdyby tento cely retezec selhal.

A k tem vyjimkam v destruktoru: kdyz pisete kod pro sebe, delejte si co chcete.
Pokud chcete, aby ten kod pouzil jeste nekdo jiny, tak jen popisem, jak to vlastne funguje a dokazovanim a vysvetlovanim proc, stravite vice casu, nez to napsat jinak :) Nehlede na to, ze za 5 let ten kod bude chtit nekdo pouzit v jinem kontextu, a on fungovat nebude. Proc? To uz ten clovek nedohleda a radsi si napise vsechno znova.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Sten 11. 02. 2014, 12:25:17
~Transaction zavolá rollback. Nejspíš pouze v případě, že poslední metoda nebyl úspěšný commit. Může házet třeba, pokud vypadne spojení s databází (jak mi tady O.N. před časem napsal). Rozumné databáze v takovém případě udělají rollback samy. Takže destruktor vlastně udělal přesně to, co měl, ale stejně vyletí výjimka. Já bych tu chybu teda hlásil až ve chvíli, kdy by uživatel té knihovny to spojení chtěl použít znova. Pokud nechce, tak vypadlé spojení ničemu nevadí.

Já při této situaci házel, pokud nebyla transakce explicitně commitnuta nebo rollbacknuta a destruktor se nevolal při stack unwindu (kdy se dělal implicitní rollback), protože ten kód byl evidentně vadný. Ale samozřejmě záleží, jakým způsobem se s tím (ne)má aplikace vyrovnávat, já mívám u serverů podmínku, že assert během requestu nesmí opustit handler requestu (takže místo abortu vyhazuje výjimku, jako to dělá Java).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 11. 02. 2014, 21:36:22
Ještě bych uvedl jednu dobrou věc na výjimkách obecně. Od té doby, co je používám jako výhradní způsob hlídání chyb mi odpadla jakákoliv práce s vytvářením systémů chybových kódů. Výjimka je samo popisná. Moje výjimky dokonce (povinně) musí hlásít i místo vzniku (__FILE__ a __LINE__) - naštěstí na to mám makro.

Kód: [Vybrat]
throw FileNotFoundException(THISLOCATION, filename);(ano, je to to makro THISLOCATION, které inicializuje objekt ProgramLocation)

- nevěřili byste jak je to užitečné. Na serverech je to užitečné vždycky, protože když se něco stane, v logu je to většinou dost popsané. Ale já to používám i na desktopových aplikacích. Každá aplikace generuje nějaký log. Někdy je ten log ukládaný jen do paměti, jindy i na disku. Pokud aplikace začne zlobit a uživatel bombarduje helpdesk svými dotazy a stížnostmi, helpdesku zpravidla stačí zaslat log. No a buď sám helpdesk z logu pozná, o co jde (jedná se třeba o částý problém), nebo to poznám později já.

Všimněte si další vlastnost
. Já s výjimkou ukládám i jméno souboru, které se nepodařilo otevřít. Tohle je prakticky nemožné zařídit systémem návratových hodnot. Pak vám aplikace nahládí "něco selhalo" nebo "soubor se nepodařilo otevřít" nebo "file not found" a nebo vám vrátí exit code 2. A teď hledejte, co se nezdařilo.

Další vlastností je výjimková hierarchie. Vytváření kategoríí výjimek. Mám základní sadu výjimek které dědí Exception. Pak výjimky ze skupiny SystemException nebo NetworkException. Threadové výjimky jsou zpravidla pod ThreadException. Souborové výjimky jsou pod IOException.

Samozřejmě mě v tom inspirovala Java (jako většina věcí)

Protože kategorie mají povinnost dědit virtuálně Exception, je povoleno dědit z vícero kategorii.

Kód: [Vybrat]
class MujProblemSeSiti: public SystemException, public NetworkException {
...
};
Já si pak na místě, kde to potřebuju mohu vybrat zda napíšu

Kód: [Vybrat]
catch(MujProblemSeSiti &e)

catch(NetworkException &e)

catch(SystemException &e)

Kód: [Vybrat]
catch(FileNotFoundException &e)

catch (IOException &e)
nebo když nevím

Kód: [Vybrat]
catch (Exception &e)
a nebo velimi obecné (odchytne drtivou většinu výjímek)

Kód: [Vybrat]
catch (std::exception &e)
handler na základní úrovni  by měl být schopen odchytnout i jinak nerozpoznanou výjimku (které ale nesmí v mých programech nikdo házet, pouze ve speciálních případech, pokud je cílem, aby výjimka nebyla odchytnuta a zahozena)

Kód: [Vybrat]
catch (...)
Jak tady někdo psal, že to sklouzává k  tomu, že výjimky se nechávají propadnout až na základní úroveň, místo aby je někdo zpracoval, tak to teda není pravda. Zpravidla na tom rozhodujícím místě dochází k větvení

Kód: [Vybrat]
catch(Vyjimka1 &e) { reportError(333, e);}
catch(Vyjimka2 &e) {reportError(666, e);}
catch(ObecnejsiVyjimka &e) {reportError(500, e);}
catch(std::exception &e) {reportGeneralError(e.what());}

Pokud někomu vadí, že musí generovat hromadu catch handlerů na konci obsluhy každého handleru, tak nemusí, dá se to udělat takto:

Kód: [Vybrat]
void processErrors() {
try {
  throw;
} catch (A a) {
...
} catch (B a) {
...
} catch (C a) {
...
} catch (D a) {
...
}
}

void handler () {
try {
....
} catch (...) {
 processErrors();
}
}

Zpět na začátek. Hlavně jsem se zbavil definování tisíců chybových kódů a hlídání toho, zda někde nekolidují. Každá knihovna měla vlastní kódy. Díky výjimkám je každá chyba přesně specifikována a zařazena do kategorie. Uvnitř aplikace létají výjimky a na vnějším rozhraní... pokud si to zadavatel přeje ... mám každé výjimkové třídě přidělen nějaký unikátní kód. A protože se to zpravidla řeší na jednom místě, není problém udržet ten seznam kódů bez kolizí.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 11. 02. 2014, 21:45:33
Zajimave videt, jak se "high availability" da pojmout ruzne. U nas to znamena nasledujici:
- Zadna dynamicka alokace pameti. Nikdy.

To asi porovnáváme neporovnatelné. Věřte mi nebo ne, ale moje knihovny v současném návrhu (i s výjimkami) se například vůbec nedají použít v Arduinu. A to ani kdybych ty výjimky nějak vyházel. Prostě tam se programuje naprosto jinak a jen díky tomu, že to nedělám denně, nemám na to vytvořené žádné kopyto (konečně o kopytech to celé je, záleží kde a co šijete)
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 11. 02. 2014, 21:49:51
to ze se na poolove operaci nebude provadet zadne dynamicke new/delete beru za samozrejmost, k tomu vetsinou slouzi ty ruzne pametove ci kontextove pooly:-)
ono je taky fajn,pokud chces at nejaky program bezi dele nez tyden, tak se celkove vyvarovat jakyhkoli dynamickych alokaci,pokud je to principialne mozne....

To mě fakt netrápí. Server má 4GB paměti, aplikace zabírá s bídou 100MB (RES). Pokud už padá, tak kvůli něčemu jinému, než kvůli paměti.

(jo mám tam watchdog, který ho rychle nahodí zpět, takže 99.9999999% uživatelů to nepozná, takže nám to jen kazí statistiky).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Tomáš Marný 11. 02. 2014, 23:10:03
Ještě bych uvedl jednu dobrou věc na výjimkách obecně. Od té doby, co je používám jako výhradní způsob hlídání chyb mi odpadla jakákoliv práce s vytvářením systémů chybových kódů
...
Zpět na začátek. Hlavně jsem se zbavil definování tisíců chybových kódů a hlídání toho, zda někde nekolidují. Každá knihovna měla vlastní kódy. Díky výjimkám je každá chyba přesně specifikována a zařazena do kategorie. Uvnitř aplikace létají výjimky a na vnějším rozhraní... pokud si to zadavatel přeje ... mám každé výjimkové třídě přidělen nějaký unikátní kód. A protože se to zpravidla řeší na jednom místě, není problém udržet ten seznam kódů bez kolizí.

Výjimky jsou jako záchranné sítě: umožňují vám dělat prasárny na laně nad propastí, neboť víte, že si přitom nezlomíte vaz, maximálně do nich spadnete. Jenže existují oblasti programování, kde si prostě nemůžete dovolit spadnout za žádnou cenu (resp. ta cena je velmi vysoká). Tam jsou vám všechny ty výjimky na nic, protože hrabat se z těch sítí zpátky na místo, odkud jste do nich slítli, je dost náročné.
Vyhazovat výjimky z destruktorů je jako připouštět, že i tu síť jste v zápalu svého prasení ukotvili jen prasecky. Říkejte si, co chcete, ale to už je oberprasárna.

Zpracovávat chybový stav jinde, v jiném kontextu, než kde nastal, je prasárna. Jak už tu padlo, principiálně totiž žádný chybový stav vlastně neexistuje. Je to prostě jen jeden z možných stavů a tedy neexistuje žádný rozumný důvod, proč kvůli němu skákat někam mimo kontext. To už výjimky můžete rovnou použít k předávání návratových hodnot. Výjimku bych chápal tradičně, tj. jako stav, s nímž návrh nepočítal - výjimečný stav, který je třeba ošetřit zvláštním způsobem.

Čím jsem starší tím větší mám pocit, že výjimky nejsou ničím jiným, než jen dalším vynálezem, kterým přiblížit programování nižším primátům. A jak to tak u takových vynálezů chodí, i výjimky fungují na principu, že z jednoduchých věcí dělají primitivní, a z obtížných ku..vsky zamotané. Ta předřečníkova adorace výjimek mi připadá, jako když projektant města vysvětluje, že když jdete po ulici a spadnete do nezadeklovaného kanálu, kterých tam nechal rozeseto mraky, tak výjimky způsoběj, že tam za vámi zahučí i taška a kočárek s dítětem a budete-li mít štěstí a přitom shazování toho kočárku dotyčný shazovatel sám nezahučí do nějakého kanálu, tak se postupně přes systém stok a výpustí můžete dostat zpátky na tu ulici, kde jste spadli. Ušetří se tak za dekly na kanály a to se vyplatí!

Žádné výjimky neudělají program spolehlivějším, ani - ač se to tak na první pohled může zdát - přehlednějším. Spíš naopak. Receptem na robustnost je praxe vývojáře a 3 věci: 1) testování (unit), 2) testování (alfa), 3) testování (beta). Ale můj pohled je dán oborem, v němž celý život působím, tj. embedded aplikace a průmyslové a telekomunikační systémy, kde up-timy jsou spíše roky než měsíce a každý pád stojí peníze - nejen pomyslně, ale reálné peníze, o něž se s vámi klient bude ochoten soudit. To není jako práce s buzolou, děti.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 11. 02. 2014, 23:59:27
Jenže existují oblasti programování, kde si prostě nemůžete dovolit spadnout za žádnou cenu (resp. ta cena je velmi vysoká). Tam jsou vám všechny ty výjimky na nic, protože hrabat se z těch sítí zpátky na místo, odkud jste do nich slítli, je dost náročné.
Výjimka != pád

Prosím naučte se to rozlišovat. Dokonce synchronní výjimky jsou prostě jinou cestou návratu (ten návrat je řízený). Tzv. asynchroní výjimky (výjimka může vzniknout kdekoliv, třeba v signal handleru) já nepoužívám a nikdy nebudu. Výjimka je prostě jen způsob, jak opustit funkci jinudy. Je to něco jiný typ returnu. Nic víc.

Zpracovávat chybový stav jinde, v jiném kontextu, než kde nastal, je prasárna. Jak už tu padlo, principiálně totiž žádný chybový stav vlastně neexistuje. Je to prostě jen jeden z možných stavů a tedy neexistuje žádný rozumný důvod, proč kvůli němu skákat někam mimo kontext.
Vy musíte být geniální programátor, že jste schopen ve svém programu podchytit všechny chybové stavy? Jen si vemte, kolik chybových stavů mají všechna systémová volání, které ve svých programech používáte. Opravdu jste schopen všechny tyhle stavy ošetřit. Na všech úrovních (!)?. A máte jistotu, že to někdo ošetřuje nad vámi (ve hierarchii callstacku)?

To už výjimky můžete rovnou použít k předávání návratových hodnot.
To teda nemůžete. To byste byl teprve prase. Výjimky ani nejsou dělané na takovou věc. Už jen optimalizace kódu se provádí na standardní běh, zatímco zpracování výjimky bývá často velice pomalé (při porovnání s běžným chodem). Naopak, když výjmky nepoužijete a ošetřujete chybové stavy každého volání, program v normálním běhu moc optimální nebude. Každý if znamená jeden odhadnutý skok, který se nemusí vždy povést správně odhadnout a procesor se vám tam zastaví na vyprazdňování fronty. Výjimky tímto netrpí.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 12. 02. 2014, 09:46:12
Vy musíte být geniální programátor, že jste schopen ve svém programu podchytit všechny chybové stavy? Jen si vemte, kolik chybových stavů mají všechna systémová volání, které ve svých programech používáte. Opravdu jste schopen všechny tyhle stavy ošetřit. Na všech úrovních (!)?. A máte jistotu, že to někdo ošetřuje nad vámi (ve hierarchii callstacku)?
Ošetřit všechny chybové stavy přece není o genialitě, ale o důslednosti. Prostě si navrhnu nějaké rozhraní a při implementaci kouknu do dokumentace na všechno co volám a ošetřuju všechny možné běžné i vzácné stavy. Všechno s čím se dokážu vypořádat na fleku se vypořádám tam. To, co nedám, předám ven. V některých případech to znamená i úpravy rozhraní. Tohle je postup, který nezávisí na tom, jestli se používají výjimky nebo chybové kódy.

Pokud používám chybové kódy, tak moje rozhraní musí předat ven všechny chyby, se kterými se nedokážu vypořádat uvnitř.
Pokud používám výjimky, tak musím minimálně v rozhraní zdokumentovat všechno, co může vyletět z funkcí, které volám uvnitř. Případně můžu ty výjimky vyhozené uvnitř vylepšit nebo konvertovat na něco, co bude venku dávat větší smysl.
O tom, jestli je to víc práce s kódy nebo s výjimkami se dá diskutovat. Ale ta stěžejní část, kdy musím kouknout do manuálu a všechny možné chyby promyslet, je u obou přístupů úplně stejná. Zbytek už je vlastně jen mechanická práce.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: randolf 12. 02. 2014, 09:57:45
Zajimave videt, jak se "high availability" da pojmout ruzne. U nas to znamena nasledujici:
- Zadna dynamicka alokace pameti. Nikdy.

To asi porovnáváme neporovnatelné. Věřte mi nebo ne, ale moje knihovny v současném návrhu (i s výjimkami) se například vůbec nedají použít v Arduinu. A to ani kdybych ty výjimky nějak vyházel. Prostě tam se programuje naprosto jinak a jen díky tomu, že to nedělám denně, nemám na to vytvořené žádné kopyto (konečně o kopytech to celé je, záleží kde a co šijete)
Ale zajiste, ja reagoval pouze na Belzebuba, ne na Vas prispevek. Trosku off-topic, uznavam.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: randolf 12. 02. 2014, 10:04:37
Ještě bych uvedl jednu dobrou věc na výjimkách obecně. Od té doby, co je používám jako výhradní způsob hlídání chyb mi odpadla jakákoliv práce s vytvářením systémů chybových kódů
...
Zpět na začátek. Hlavně jsem se zbavil definování tisíců chybových kódů a hlídání toho, zda někde nekolidují. Každá knihovna měla vlastní kódy. Díky výjimkám je každá chyba přesně specifikována a zařazena do kategorie. Uvnitř aplikace létají výjimky a na vnějším rozhraní... pokud si to zadavatel přeje ... mám každé výjimkové třídě přidělen nějaký unikátní kód. A protože se to zpravidla řeší na jednom místě, není problém udržet ten seznam kódů bez kolizí.

Výjimky jsou jako záchranné sítě: umožňují vám dělat prasárny na laně nad propastí, neboť víte, že si přitom nezlomíte vaz, maximálně do nich spadnete. Jenže existují oblasti programování, kde si prostě nemůžete dovolit spadnout za žádnou cenu (resp. ta cena je velmi vysoká). Tam jsou vám všechny ty výjimky na nic, protože hrabat se z těch sítí zpátky na místo, odkud jste do nich slítli, je dost náročné.
Vyhazovat výjimky z destruktorů je jako připouštět, že i tu síť jste v zápalu svého prasení ukotvili jen prasecky. Říkejte si, co chcete, ale to už je oberprasárna.

Zpracovávat chybový stav jinde, v jiném kontextu, než kde nastal, je prasárna. Jak už tu padlo, principiálně totiž žádný chybový stav vlastně neexistuje. Je to prostě jen jeden z možných stavů a tedy neexistuje žádný rozumný důvod, proč kvůli němu skákat někam mimo kontext. To už výjimky můžete rovnou použít k předávání návratových hodnot. Výjimku bych chápal tradičně, tj. jako stav, s nímž návrh nepočítal - výjimečný stav, který je třeba ošetřit zvláštním způsobem.

Čím jsem starší tím větší mám pocit, že výjimky nejsou ničím jiným, než jen dalším vynálezem, kterým přiblížit programování nižším primátům. A jak to tak u takových vynálezů chodí, i výjimky fungují na principu, že z jednoduchých věcí dělají primitivní, a z obtížných ku..vsky zamotané. Ta předřečníkova adorace výjimek mi připadá, jako když projektant města vysvětluje, že když jdete po ulici a spadnete do nezadeklovaného kanálu, kterých tam nechal rozeseto mraky, tak výjimky způsoběj, že tam za vámi zahučí i taška a kočárek s dítětem a budete-li mít štěstí a přitom shazování toho kočárku dotyčný shazovatel sám nezahučí do nějakého kanálu, tak se postupně přes systém stok a výpustí můžete dostat zpátky na tu ulici, kde jste spadli. Ušetří se tak za dekly na kanály a to se vyplatí!

Žádné výjimky neudělají program spolehlivějším, ani - ač se to tak na první pohled může zdát - přehlednějším. Spíš naopak. Receptem na robustnost je praxe vývojáře a 3 věci: 1) testování (unit), 2) testování (alfa), 3) testování (beta). Ale můj pohled je dán oborem, v němž celý život působím, tj. embedded aplikace a průmyslové a telekomunikační systémy, kde up-timy jsou spíše roky než měsíce a každý pád stojí peníze - nejen pomyslně, ale reálné peníze, o něž se s vámi klient bude ochoten soudit. To není jako práce s buzolou, děti.

Vpodstate s Vami souhlasim. Predpokladam ovsem, ze pokud by puvodni tazatel chtel delat kod pro takovou aplikaci, tak by vyjimky nepouzil. Pokud je jeho cilem vlastni aplikace pro dejme tomu sledovani poctu vypranych ponozek, je to asi OK, ne? At si je klidne pouzije :)
Název: Re:C++ a výjimka
Přispěvatel: Ondřej Novák 12. 02. 2014, 10:31:00
Ošetřit všechny chybové stavy přece není o genialitě, ale o důslednosti. Prostě si navrhnu nějaké rozhraní a při implementaci kouknu do dokumentace na všechno co volám a ošetřuju všechny možné běžné i vzácné stavy. Všechno s čím se dokážu vypořádat na fleku se vypořádám tam. To, co nedám, předám ven. V některých případech to znamená i úpravy rozhraní. Tohle je postup, který nezávisí na tom, jestli se používají výjimky nebo chybové kódy.
No máte a nemáte pravdu. Samozřejmě, že chybové stavy vyplývající z činnosti volaného programu/metody/funkce/whatever by mělo být ošetřeno uvnitř a vypadávat ven tak, jak bylo navrženo. Chybové stavy nějakého stavového automatu jsou stavy toho automatu, ne výjimky (k tomu se výjimky nepoužívají).

Nevím, kolik máte za sebou praxe, ale určitě víte, že člověk málokdy píše konečnou funkcionalitu, která dále nezávisí na jiných neznámých. Jednou neznámou je třeba fungování platformy. Proto jsem se ptal, jestli jsou mezi námi tak geniální nebo důslední programátoři, že jsou schopni ošetřit všechny chybové stavy. Protože považte, kolik chybových stavů má jenom funkce fopen

http://www.root.cz/man/3/fopen/

Druhou neznámou jsou dodané objekty z venku, které mohou neočekávaně selhat. Například callback funkce, handlery všelijakého druhu a podobně. Je třeba mít mechanismus, jak s tím naložit

Pokud používám chybové kódy, tak moje rozhraní musí předat ven všechny chyby, se kterými se nedokážu vypořádat uvnitř.
Jak zajišťujete nekolidování chybových kódů


Pokud používám výjimky, tak musím minimálně v rozhraní zdokumentovat všechno, co může vyletět z funkcí, které volám uvnitř. Případně můžu ty výjimky vyhozené uvnitř vylepšit nebo konvertovat na něco, co bude venku dávat větší smysl.
O tom, jestli je to víc práce s kódy nebo s výjimkami se dá diskutovat. Ale ta stěžejní část, kdy musím kouknout do manuálu a všechny možné chyby promyslet, je u obou přístupů úplně stejná. Zbytek už je vlastně jen mechanická práce.

To je otázkou, zda vnitřní výjimky odchytávat a přebalovat je na nějaké vnější výjimky, které jsem si  pro tento účel vytvořil. Má to několik háčků.


I tady je třeba postupovat opatrně a z rozvahou.


Název: Re:C++ a výjimka
Přispěvatel: belzebub 12. 02. 2014, 14:31:43
Ošetřit všechny chybové stavy přece není o genialitě, ale o důslednosti. Prostě si navrhnu nějaké rozhraní a při implementaci kouknu do dokumentace na všechno co volám a ošetřuju všechny možné běžné i vzácné stavy. Všechno s čím se dokážu vypořádat na fleku se vypořádám tam. To, co nedám, předám ven. V některých případech to znamená i úpravy rozhraní. Tohle je postup, který nezávisí na tom, jestli se používají výjimky nebo chybové kódy.
No máte a nemáte pravdu.
Ma pravdu. Tecka.

Nevím, kolik máte za sebou praxe, ale určitě víte, že člověk málokdy píše konečnou funkcionalitu, která dále nezávisí na jiných neznámých. Jednou neznámou je třeba fungování platformy. Proto jsem se ptal, jestli jsou mezi námi tak geniální nebo důslední programátoři, že jsou schopni ošetřit všechny chybové stavy. Protože považte, kolik chybových stavů má jenom funkce fopen

Nemuzu mluvit za JSH, ale za me: neni nutne osetrit vsechny mozne stavy individualne. Zalezi na stavech, ktere jsou pro danou aplikaci relevantni. Pokud aplikace nepotrebuje zadnym zpusobem vedet, proc se nepodarilo soubor otevrit, pak takove osetreni resi pouze 2 stavy: OK/ERROR. To ze "ERROR" v sobe obsahuje bazilion ruznych chyb je v tomto pripade mozne ignorovat. Podstatne je pouze osetrit CELY stavovy prostor - jak detailne uz zavisi na tom, co od aplikace ocekavame.
S timto pristupem neni reseni chyb nijak komplikovane ani s chybovymi kody.

Druhou neznámou jsou dodané objekty z venku, které mohou neočekávaně selhat. Například callback funkce, handlery všelijakého druhu a podobně. Je třeba mít mechanismus, jak s tím naložit
Takove objekty musi mit jasne definovane rozhrani - a tedy zpusob jak zjistit zda selhaly (a opet je jedno jestli nejakym try/catch nebo osetrenim errorcodu). Ve vetsine pripadu staci brat opet pouze 2 stavy - externi objekt selhal, nebo neselhal. Osetreni opet trivialni.

Jak zajišťujete nekolidování chybových kódů
Jak zajistujete iteraci pres seznam? Stejne jako na tuto otazku, odpovedi muze byt mnoho. Co treba:

1) Ja jsem zminoval glib - jeji GError resi nejen nekolidovani (internovani textovych identifikatoru chyby pri jeji definici) ale i hierarchii (plus prilozeni dalsich informaci k chybe, coz jste z me neznamych duvodu pokladal za nejakou vymozenost vyjimek)
2) rucne v jednom enum-u v jednom globalnim hlavickovem souboru pro chyby (idealne automaticky generovanem z nejakeho textaku kde je k chybe doplnen i jeji lokalizovatelny textovy popis)
3) "neresim" - kazdy "modul" (napr. foo.c, foo.h) ma sve errorkody s prefixem "ERROR_FOO_" - jejich skutecna integer hodnota nemusi byt nijak unikatni, protoze selhani modulu "foo" musi resit kod, ktery ho vola a protoze "foo" muze vratit pouze "ERROR_FOO_*" kody, neni zadny duvod pro jejich unikatnost.
4) za domaci ukol si kazdy muze vymyslet sve vlastni reseni :)



To je otázkou, zda vnitřní výjimky odchytávat a přebalovat je na nějaké vnější výjimky, které jsem si  pro tento účel vytvořil. Má to několik háčků.
S tim nesouhlasim. Prebaleni chyby/vyjimky je NUTNE, pokud chci mit rozume rozdelene zodpovednosti objektu/modulu, pokud chci nejake rozumne zapouzdreni a hlavne pokud nezapouzdrim, tak to je naprosto nezadouci zavislost rozhrani meho objektu/modulu na nizsich objektech/modulech, ktera proteka az do nejvyssi urovne.
To je asi jakoby Vam Firefox pri nezdarenem pokusu o otevreni stranky vypsal HTTP odpoved, TCP/IP packety, ethernetove pakety, atd, atd.
Myslim, ze to co povazujete za "hacek" vesmes resi logovani.
Chybovy kod/vyjimka musi programu pouze umoznit se pri chybe vydat "jinou cestou" nez bez chyby. Opet je to o duslednosti a opet je to o spravnem rozdeleni stavoveho prostoru - pokud pouziji vyse zmineny "fopen": budu mit "konfiguracni" objekt, ktery precte a rozparsuje konfiguracni soubor, tak prestoze "fopen" muze vratit mnozstvi chyb, v 99% pripadu bude mit muj "konfiguracni" soubor pouze jeden chybovy stav pro selhavsi "fopen" - neco ve stylu: ERROR_CONFIG_OPEN("Couldn't open configuration file myapp.cfg"). Presny errorcode proc "fopen" selhal neni nutne uvadet. A pokud aplikace VYZADUJE aby se rozlisila napr. chyba s pravy uzivatele, tak se prida tato chyba. Ale je dulezite, aby se konfiguracni objekt zvenku choval jako blackbox s PRESNE definovanym chovanim a chybovymi stavy. Detaily pak lze nalezt v logu.


Pane Novak, nechci Vas nebudu obvinovat z neznalosti, nedostatecnych "1337 skillz", nebo podobne. Ale ja jsem v C delal uz na 3 netrivialnich projektech, a vetsinu Vasich namitek co "nejde" bez vyjimek, jsem mnohokrat a bez vetsich problemu resil.
Ale tim, ze "predpokladate", ze to v C nejde (protoze delate v C++), se stavite presne do te pozice cloveka, ktery neco nezna, nepouziva to, ale je presvedcen ze o tom vse vi a vi proc je to spatne a proc je to co pouziva on lepsi.
Název: Re:C++ a výjimka
Přispěvatel: Sten 12. 02. 2014, 14:54:53
Někdy může být problém zdokumentovat všechny možné výjimky letící ven, když není známo v jakém prostředí bude algoritmus fungovat. Například se vám může stát, že vám někdo podstrčí virtuální filesystem, na kterém létají naprosto odlišné výjimky, než jste zvyklý - (má knihovna má přístup na fs přes rozhraní, které jde přepsat vlastní verzí, aniž by na první pohled bylo poznat, že program nepracuje s fyzickým diskem, nebo s fyzickou sítí, například)

Tohle se ale týká i chybových kódů. Není neobvyklé, že některé funkce v libc dnes vracejí i jiné chyby, než byly v dokumentaci v době psaní programu (třeba open na Linuxu vrací navíc oproti POSIXu EWOULDBLOCK). Prostě je potřeba počítat i s tím, že to selže jinak, než je v dokumentaci.

Jak zajistujete iteraci pres seznam? Stejne jako na tuto otazku, odpovedi muze byt mnoho. Co treba:

1) Ja jsem zminoval glib - jeji GError resi nejen nekolidovani (internovani textovych identifikatoru chyby pri jeji definici) ale i hierarchii (plus prilozeni dalsich informaci k chybe, coz jste z me neznamych duvodu pokladal za nejakou vymozenost vyjimek)
2) rucne v jednom enum-u v jednom globalnim hlavickovem souboru pro chyby (idealne automaticky generovanem z nejakeho textaku kde je k chybe doplnen i jeji lokalizovatelny textovy popis)
3) "neresim" - kazdy "modul" (napr. foo.c, foo.h) ma sve errorkody s prefixem "ERROR_FOO_" - jejich skutecna integer hodnota nemusi byt nijak unikatni, protoze selhani modulu "foo" musi resit kod, ktery ho vola a protoze "foo" muze vratit pouze "ERROR_FOO_*" kody, neni zadny duvod pro jejich unikatnost.
4) za domaci ukol si kazdy muze vymyslet sve vlastni reseni :)

Už jsem viděl i řešení, které vracelo const char* se statickým popiskem chyby (NULL pokud nic neselhalo). Přišlo mi to jako v podstatě geniální řešení chybových kódů, které řeší popisky i unikátnost kódů :-)

S tim nesouhlasim. Prebaleni chyby/vyjimky je NUTNE, pokud chci mit rozume rozdelene zodpovednosti objektu/modulu, pokud chci nejake rozumne zapouzdreni a hlavne pokud nezapouzdrim, tak to je naprosto nezadouci zavislost rozhrani meho objektu/modulu na nizsich objektech/modulech, ktera proteka az do nejvyssi urovne.
To je asi jakoby Vam Firefox pri nezdarenem pokusu o otevreni stranky vypsal HTTP odpoved, TCP/IP packety, ethernetove pakety, atd, atd.
Myslim, ze to co povazujete za "hacek" vesmes resi logovani.
Chybovy kod/vyjimka musi programu pouze umoznit se pri chybe vydat "jinou cestou" nez bez chyby. Opet je to o duslednosti a opet je to o spravnem rozdeleni stavoveho prostoru - pokud pouziji vyse zmineny "fopen": budu mit "konfiguracni" objekt, ktery precte a rozparsuje konfiguracni soubor, tak prestoze "fopen" muze vratit mnozstvi chyb, v 99% pripadu bude mit muj "konfiguracni" soubor pouze jeden chybovy stav pro selhavsi "fopen" - neco ve stylu: ERROR_CONFIG_OPEN("Couldn't open configuration file myapp.cfg"). Presny errorcode proc "fopen" selhal neni nutne uvadet. A pokud aplikace VYZADUJE aby se rozlisila napr. chyba s pravy uzivatele, tak se prida tato chyba. Ale je dulezite, aby se konfiguracni objekt zvenku choval jako blackbox s PRESNE definovanym chovanim a chybovymi stavy. Detaily pak lze nalezt v logu.

Tohle má ale jednu velkou výjimku (pun intended :) ): callbacky. V případě, kdy dojde k chybě v callbacku, tak přebalením zabráníte volajícímu poznat, co se stalo, a nepřebalením zase rozbijete contract. I když, u výjimek to jde zabalit jako CallbackFailed a původní výjimku vložit do ní a u error codů jde vyhradit nějaký rozsah pro chyby callbacků.
Název: Re:C++ a výjimka
Přispěvatel: Ondřej Novák 12. 02. 2014, 15:40:31
pouze 2 stavy: OK/ERROR. To ze "ERROR" v sobe obsahuje bazilion ruznych chyb je v tomto pripade mozne ignorovat. Podstatne je pouze osetrit CELY stavovy prostor - jak detailne uz zavisi na tom, co od aplikace ocekavame.
S timto pristupem neni reseni chyb nijak komplikovane ani s chybovymi kody.
Znám spoustu programů, které, když se jim něco nelíbí, tak napíšou "ERROR" a skončí.

Takove objekty musi mit jasne definovane rozhrani - a tedy zpusob jak zjistit zda selhaly (a opet je jedno jestli nejakym try/catch nebo osetrenim errorcodu). Ve vetsine pripadu staci brat opet pouze 2 stavy - externi objekt selhal, nebo neselhal. Osetreni opet trivialni.

Samozřejmě, že je ošetření trivální. ERROR  ;D

1) Ja jsem zminoval glib - jeji GError resi nejen nekolidovani (internovani textovych identifikatoru chyby pri jeji definici) ale i hierarchii (plus prilozeni dalsich informaci k chybe, coz jste z me neznamych duvodu pokladal za nejakou vymozenost vyjimek)
Vidíte! A ony přesně výjimky takhle fungují. Jediný co mají navíc je to, že nemusíte psát kolem volání IFy
Kód: [Vybrat]
if (!foo()) return 0;
if (!bar()) return 0;
if (!fee()) return 0;
pokud nastane chyba ve foo, tak vyhozená výjimka jedná ve stejném duchu, jako výše uvedený zápis, udělá return s nějakým chybovým příznakem, aby nadřazená funkce věděla, že nastala chyba. Vám ušetří psaní. Informace o výjimce se drží v nějaké statické TLS proměnné, odkud se pak vyzvedávají v handleru.

Proč teda vy lidi máte takovou averzi vůči výjimkám, když jde jen o syntax-suggar?

Výhodou výjimek je to, že fungují stejně třeba i ve Windows, kde není glib a ani GError
2) rucne v jednom enum-u v jednom globalnim hlavickovem souboru pro chyby (idealne automaticky generovanem z nejakeho textaku kde je k chybe doplnen i jeji lokalizovatelny textovy popis)
Globální hlavičkový soubor... vy asi nepíšete knihovny?

3) "neresim" - kazdy "modul" (napr. foo.c, foo.h) ma sve errorkody s prefixem "ERROR_FOO_" - jejich skutecna integer hodnota nemusi byt nijak unikatni, protoze selhani modulu "foo" musi resit kod, ktery ho vola a protoze "foo" muze vratit pouze "ERROR_FOO_*" kody, neni zadny duvod pro jejich unikatnost.
A z modulu FOO vyhodíte chybu do modulu BAR tak že nadefinujete proměnnou ERROR_BAR_ERROR_IN_FOO a tu zareportujete ven. Co se vlastně ve FOO stalo, to už se nikdo nedozví. Když to neuděláte vy, tak to udělá váš nějaký méně zdatný kolega. Nebo to udělá proto, že mu na vývoj modulu nadřizený přidělil málo času (pak je 1000x lepší, když vylítne výjimka přímoz FOO, než když ji někdo zahodí a vyhodí obecnou chybu)


To je asi jakoby Vam Firefox pri nezdarenem pokusu o otevreni stranky vypsal HTTP odpoved, TCP/IP packety, ethernetove pakety, atd, atd.
Co myslíte, jak mi je, když mi Firefox napíše, že "Stránka se nedá zobrazit" (aby třeba k tomu dodal, že DNS jméno neexistuje, nebo že selhalo spojení, což mi třeba napoví, že jméno mám správně, ale blbec jsem si nohou vykopnul kabel z ethernetu)

pouze jeden chybovy stav pro selhavsi "fopen" - neco ve stylu: ERROR_CONFIG_OPEN("Couldn't open configuration file myapp.cfg"). Presny errorcode proc "fopen" selhal neni nutne uvadet.

Opět jeden příklad z praxe. Neustále to hlásilo, že to nemůže otevřít nějaké datové soubory. Dokonce někoho chytrého napadlo, že by bylo dobré uživateli napovědět, co má dělat. Ať si prý zkontroluje, zda tam ty soubory jsou. No byly tam, ale program stále tvrdil, že tam nejsou. Až po podrobném prozkoumání problému pomocí strace se ukázalo, důvodem, proč to nejde je chyba "Access Denied". Po úpravě přístupových práv k těm souborům se to už rozběhlo. Jistě, že to někoho mohlo napadnout předtím, ale nenapadlo... celé odpoledne zabyté jen tím, že autor programu měl podobný názor jako vy.

Pane Novak, nechci Vas nebudu obvinovat z neznalosti, nedostatecnych "1337 skillz", nebo podobne. Ale ja jsem v C delal uz na 3 netrivialnich projektech, a vetsinu Vasich namitek co "nejde" bez vyjimek, jsem mnohokrat a bez vetsich problemu resil.
Ale tim, ze "predpokladate", ze to v C nejde (protoze delate v C++), se stavite presne do te pozice cloveka, ktery neco nezna, nepouziva to, ale je presvedcen ze o tom vse vi a vi proc je to spatne a proc je to co pouziva on lepsi.

Jen 3?  ;D  ;D  ;D  ;D
Název: Re:C++ a výjimka
Přispěvatel: belzebub 12. 02. 2014, 16:58:10
pouze 2 stavy: OK/ERROR. To ze "ERROR" v sobe obsahuje bazilion ruznych chyb je v tomto pripade mozne ignorovat. Podstatne je pouze osetrit CELY stavovy prostor - jak detailne uz zavisi na tom, co od aplikace ocekavame.
S timto pristupem neni reseni chyb nijak komplikovane ani s chybovymi kody.
Znám spoustu programů, které, když se jim něco nelíbí, tak napíšou "ERROR" a skončí.
Ja znam spousti lidi co maji bradavici na nose. Jak to souvisi s vyjimkami a errorkody? Nijak. Stejne jako Vase vyjadreni ze nejake programy co znate napisi "ERROR".

Takove objekty musi mit jasne definovane rozhrani - a tedy zpusob jak zjistit zda selhaly (a opet je jedno jestli nejakym try/catch nebo osetrenim errorcodu). Ve vetsine pripadu staci brat opet pouze 2 stavy - externi objekt selhal, nebo neselhal. Osetreni opet trivialni.
Samozřejmě, že je ošetření trivální. ERROR  ;D
Ne, osetreni neni "ERROR", ale nekolik "nadstavu", ktere pokryvaji mnozinu VSECH stavu. Ano, je mozne mit pouze dva "nadstavy": OK a ERROR.

Vidíte! A ony přesně výjimky takhle fungují. Jediný co mají navíc je to, že nemusíte psát kolem volání IFy
Pane Novak. Ja VIM jak funguji vyjimky. Chapu ze jste podeziravy, ale dokazete pripustit, ze chapu vyjimky v C++, v Jave, v C#, v Pythonu, delal jsem i s "vyjimkami" v CLOS, s Exception monad-ami v haskellu.
Chapu koncept vyjimek v C++. Zkuste predstirat, ze mi verite.

Proč teda vy lidi máte takovou averzi vůči výjimkám, když jde jen o syntax-suggar?
"My lidi" - to nevim koho myslite. Ale ja, clovek, k tomu mam averzi proto, ze vyjimky (v C++!! a podobnych jazycich - nemam nic proti CLOS vyjimkam nebo "vyjimkam" realizovanym pres monady v haskellu) jsou syntax-suggar, ktery usnadnuje psani spatne osetreni chybovych stavu. Tj. umoznuje psat kod, ktery VYPADA, ze je na prvni pohled cisty, krasny, jasny, ale ve skutecnosti muze byt semeniste prasaren, segfaultu a jinych radosti.

Pro ujasneni - netvrdim, ze vyjimky jsou NAPROSTO SPATNE - tvrdim, ze SPRAVNE osetrovani chybovych stavu pouzivanim vyjimek v C++ je MNOHEM SLOZITEJSI, NEZ TO VYPADA, a proto ho vetsina lidi udela SPATNE.


Výhodou výjimek je to, že fungují stejně třeba i ve Windows, kde není glib a ani GError
Prosim, budte tak laskav a neco si o glib zjistete. Jedna z hlavnich vyhod pro glib (a proc ji pouzivame), je to, ze je EXTREMNE dobre prenositelna mezi platformami - tj. na windows bezi uplne stejne dobre jako na linuxu. Je to normalni C knihovna. Dokonce nepotrebuje ani gcc.
Tj. nemate pravdu, glib s GErrorem je VSUDE, kde je C kompilator.

2) rucne v jednom enum-u v jednom globalnim hlavickovem souboru pro chyby (idealne automaticky generovanem z nejakeho textaku kde je k chybe doplnen i jeji lokalizovatelny textovy popis)
Globální hlavičkový soubor... vy asi nepíšete knihovny?
Jisteze pisi. Soubor je "globalni v ramci jedne nebo vice knihoven", stejne jako je "globalni" treba errno.h.
Opet bych Vas poprosil, abyste zkusil argumentovat fakty, a ne neustalym naznacovanim ze "nic neumim a nicemu nerozumim".

3) "neresim" - kazdy "modul" (napr. foo.c, foo.h) ma sve errorkody s prefixem "ERROR_FOO_" - jejich skutecna integer hodnota nemusi byt nijak unikatni, protoze selhani modulu "foo" musi resit kod, ktery ho vola a protoze "foo" muze vratit pouze "ERROR_FOO_*" kody, neni zadny duvod pro jejich unikatnost.
A z modulu FOO vyhodíte chybu do modulu BAR tak že nadefinujete proměnnou ERROR_BAR_ERROR_IN_FOO a tu zareportujete ven. Co se vlastně ve FOO stalo, to už se nikdo nedozví. Když to neuděláte vy, tak to udělá váš nějaký méně zdatný kolega. Nebo to udělá proto, že mu na vývoj modulu nadřizený přidělil málo času (pak je 1000x lepší, když vylítne výjimka přímoz FOO, než když ji někdo zahodí a vyhodí obecnou chybu)
V BAR zavolam FOO a ten vrati ERROR_FOO_*, tato chyba se zaloguje (tj. co se stalo ve FOO se dozvi kdokoliv, kdo se koukne do logu) a na zaklade hodnoty chyby vyberu co dal delat, a protoze, jak jsem psal o zabalovani chyb/vyjimek, vratim chybu, ktera ma VYZNAM v kontextu BAR. tj. tam kde puvodni chyba byla ERROR_FOO_DISK_FULL muzu vratit treba ERROR_BAR_STORAGE_FULL..
Mimochodem neco podobneho jsem pouzil jen nekolikrat, vzdy u dost malych knihoven, kde by pouziti glib a GError byla zbytecne "velka" zavislost. Klicove slovo je "dost malych". Pokud je kod maly, dostatecne jasne definovano co ma delat, neni toto schema (i kdybych pripustil vase ERROR_BAR_IN_FOO "retezeni") nijak problematicke.

To je asi jakoby Vam Firefox pri nezdarenem pokusu o otevreni stranky vypsal HTTP odpoved, TCP/IP packety, ethernetove pakety, atd, atd.
Co myslíte, jak mi je, když mi Firefox napíše, že "Stránka se nedá zobrazit" (aby třeba k tomu dodal, že DNS jméno neexistuje, nebo že selhalo spojení, což mi třeba napoví, že jméno mám správně, ale blbec jsem si nohou vykopnul kabel z ethernetu)
Ano, firefox Vam napovi, ale pouze z VELMI OMEZENEHO mnozstvi chybovych stavu - coz je presne to o cem mluvim - firefox Vam nenahlasi KAZDOU MOZNOU CHYBU - pouze nekolik, ktere si podle vyvojaru zaslouzi vlastni "kategorii". Pro vsechno ostatni bude "selhalo spojeni". Coz, jak rikate "napovi", nicmene se jedna o to, o cem mluvim ja - "zabaleni" vyjimky - firefox Vam nepovi o tom, jaky errorcode vratil "socket", nerekne vam na jake pozici doslo ke korupci paketu. Napise "selhalo spojeni", protoze to je ta "zabalena" chyba/vyjimka. Natoz aby Vam rekl ze jste blbec a ze jste si vykopnul kabel.
Coz souvisi s tim cemu se obcas rika "leaky abstraction" - proste kazda vyssi vrstva je ve vetsine pripadu nucena provest nejake "zobecneni" urcitych chybovych stavu pod jednu "strechu". Pak NENI mozne bez dalsich nastroju zjistit, kde k chybe doslo. To je ale normalni, a je to v poradku. Firefox nema slouzit jako diagnostika site. Na to jsou jine nastroje.

pouze jeden chybovy stav pro selhavsi "fopen" - neco ve stylu: ERROR_CONFIG_OPEN("Couldn't open configuration file myapp.cfg"). Presny errorcode proc "fopen" selhal neni nutne uvadet.
Opět jeden příklad z praxe. Neustále to hlásilo, že to nemůže otevřít nějaké datové soubory. Dokonce někoho chytrého napadlo, že by bylo dobré uživateli napovědět, co má dělat. Ať si prý zkontroluje, zda tam ty soubory jsou. No byly tam, ale program stále tvrdil, že tam nejsou. Až po podrobném prozkoumání problému pomocí strace se ukázalo, důvodem, proč to nejde je chyba "Access Denied". Po úpravě přístupových práv k těm souborům se to už rozběhlo. Jistě, že to někoho mohlo napadnout předtím, ale nenapadlo... celé odpoledne zabyté jen tím, že autor programu měl podobný názor jako vy.
NE, pletete si priklad s pristupem.

K prikladu - ano, to je bezne, a napr. me by nenapadlo "podrobne zkoumat problem pomoci strace", protoze vim ze duvodu, proc NEJDE OTEVRIT soubor, je nekolik, a chybejici prava je asi druhy, ktery bych zjistoval

K pristupu:
Ja jsem explicitne napsal (v te casti, kterou jste se rozhodl necitovat), ze pokud MA SMYSL DALSI CHYBOVY STAV Z HLEDISKA APLIKACE, tak se samozrejme prida i dalsi chybovy stav. Vase argumentace je zalozena na tom, ze jsem v prikladu, ukazujicim redukci vsech moznych stavu na STAVY VYZNAMNE PRO APLIKACI, pouzil ZAMERNE ZJEDNODUSENI.
Protoze jinak se jedne presne o to same, jako priklad s Firefoxem a vypadlym kabelem. Firefox Vam TAKE nerekne, ze vypadl kabel (i kdyz eth karta to vi!!), ale ze nemuze navazat spojeni.


Pane Novak, nechci Vas nebudu obvinovat z neznalosti, nedostatecnych "1337 skillz", nebo podobne. Ale ja jsem v C delal uz na 3 netrivialnich projektech, a vetsinu Vasich namitek co "nejde" bez vyjimek, jsem mnohokrat a bez vetsich problemu resil.
Ale tim, ze "predpokladate", ze to v C nejde (protoze delate v C++), se stavite presne do te pozice cloveka, ktery neco nezna, nepouziva to, ale je presvedcen ze o tom vse vi a vi proc je to spatne a proc je to co pouziva on lepsi.
Jen 3?  ;D  ;D  ;D  ;D
Ano, jen 3. Projekt se navrhne, napise, odladi, spusti a udrzuje. Klicove slovicko, ktere jste zjevne zamerne ignoroval je "netrivialni". Napiste mi prosim, na kolika netrivialnich projektech (rekneme nad 50 000 radek), v C (ne C++) jste se podilel Vy.
Název: Re:C++ a výjimka
Přispěvatel: Ondřej Novák 12. 02. 2014, 17:19:02
Výhodou výjimek je to, že fungují stejně třeba i ve Windows, kde není glib a ani GError
Prosim, budte tak laskav a neco si o glib zjistete. Jedna z hlavnich vyhod pro glib (a proc ji pouzivame), je to, ze je EXTREMNE dobre prenositelna mezi platformami - tj. na windows bezi uplne stejne dobre jako na linuxu. Je to normalni C knihovna. Dokonce nepotrebuje ani gcc.
Tj. nemate pravdu, glib s GErrorem je VSUDE, kde je C kompilator.

Prosím oddělme dvě věci. Jednou je Windows svět (se svým WinAPI, GDI, MFC, COM+ a jinými 3,14covinami), a podruhé je linuxový svět přeportovaný do Windows. To druhé vypadá jak pěst na voko, sorry.

Já už nebudu natahovat diskuzi na téma výjimky, nikam to nevede. Každý máme svou pravdu. Já bych se k systému bez výjimek nevracel ani omylem. Dokud tedy nemusím. Následující dva kódy ukazují, proč tomu tak je. Jedná se o program "hello world" napsaný pod mým systémem WebGUI (časem to bude openSource, možná na to bude článek - ale to je v celku jedno). První je v C++, kde se chyby ošetřují výjimkou. Druhý je napsaný v C, s patřičným ošetřením všech chyb.

Kód: [Vybrat]
#include <stdlib.h>
#include <memory>
#include <string.h>
#include "webgui/IClient.h"


static const char *hellotext = "Hello World";



int main(int argc, char **argv) {

const char *webguiaddr = getenv("WEBGUI");
if (webguiaddr == 0) {
fprintf(stderr, "WEBGUI env variable is not defined\n");
return 1;
}

try {
std::auto_ptr<webgui::IClient> client(webgui::createClient());
client->connect(webguiaddr);
client->createWindow(client->getBaseUrl());
std::auto_ptr<webgui::IRequest> r(client->getRequest(30000));
if (r.get() == 0) {
fprintf(stderr,"Timeout waiting on request\n");
} else {
r->setHeader("Content-Type","text/plain");
r->write(hellotext,strlen(hellotext));
r->closeOutput();
}

} catch (std::exception &e) {
fprintf(stderr, "%s\n", e.what());
}



}
Kód: [Vybrat]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "webgui/webgui_c.h"


static const char *hellotext = "Hello World";

static int showError() {
fprintf(stderr,"Exception: %s\n",webgui_getLastErrorStr());
webgui_clearError();
return 100;
}


int main(int argc, char **argv) {

WEBGUI_STR baseUrl;
WEBGUI_CLIENT client;
WEBGUI_REQUEST r;
int err = 0;
const char *webguiaddr = getenv("WEBGUI");
//if doesnt exist, report error
if (webguiaddr == 0) {
fprintf(stderr, "WEBGUI env variable is not defined\n");
return 1;
}

client = webgui_client_create();
do {
if (!webgui_connect(client,webguiaddr)) break;
if (!webgui_getBaseUrl(client,&baseUrl)) break;
if (!webgui_createWindow(client,&baseUrl)) break;
r = webgui_getRequest(client,30000);
if (r == 0) {
   fprintf(stderr,"Timeout waiting on request\n");
} else {
do {
if (!webgui_setHeader(r,"Conten-Type","text/plain")) break;
if (!webgui_write(r,hellotext,strlen(hellotext))) break;
if (!webgui_closeOutput(r))  break;
} while (0);
if (webgui_getLastErrorStr() != 0) {
err = showError();
}
webgui_closeRequest(r);
}
} while (0);

if (webgui_getLastErrorStr() != 0)
err = showError();

webgui_client_destroy(client);
return err;



}


Pár poznámek k tomu co není vidět. C rozhraní je samozřejmě wraperem původního C++. Výjimky se tedy tady maskují ve funkci webgui_getLastErrorStr(). což je asi jediný rozumný způsob, jak přistupovat k chybě. Je pochopitelné, že proměnná, která drží chybu musí být vnitřně deklarovaná přes __thread (kdyby více vláken). Nemám tu chybové kódy, protože v celké knihovně není jediný (ale mám tam webgui_getLastErrorType(), která vrací řetězec identifikující typ chyby.

Já nevím, osobně mě to přijde jako hádat se, zda v HTML používat nebo nepoužívat tabulkový design.
Název: Re:C++ a výjimka
Přispěvatel: Ondřej Novák 12. 02. 2014, 17:25:33
Ano, jen 3. Projekt se navrhne, napise, odladi, spusti a udrzuje. Klicove slovicko, ktere jste zjevne zamerne ignoroval je "netrivialni". Napiste mi prosim, na kolika netrivialnich projektech (rekneme nad 50 000 radek), v C (ne C++) jste se podilel Vy.

Nevím, nepočítal jsem to, budou to desítky. Když vezmu jen rok 1996-2000, tak ty tři hravě překonám (těch 50tis řádek beru jako rozhodující míru)
Název: Re:C++ a výjimka
Přispěvatel: Ondřej Novák 12. 02. 2014, 17:28:05
Ano, jen 3. Projekt se navrhne, napise, odladi, spusti a udrzuje. Klicove slovicko, ktere jste zjevne zamerne ignoroval je "netrivialni". Napiste mi prosim, na kolika netrivialnich projektech (rekneme nad 50 000 radek), v C (ne C++) jste se podilel Vy.

Proč jenom v C? V C jsem napsal asi dva až tři velké projekty, když jsem zjistil, že takhle to dál nejde, a komplet jsem přesedlal na C++, kde mám spoustu projektů. Prostě nebudu se vracet ke starým technologiím, dokud vyloženě nemusím (naposledy při portování Skeldalu na iPhone, naštěstí jsem jen vypomáhal)
Název: Re:C++ a výjimka
Přispěvatel: Sten 12. 02. 2014, 18:33:30
Ano, firefox Vam napovi, ale pouze z VELMI OMEZENEHO mnozstvi chybovych stavu - coz je presne to o cem mluvim - firefox Vam nenahlasi KAZDOU MOZNOU CHYBU - pouze nekolik, ktere si podle vyvojaru zaslouzi vlastni "kategorii". Pro vsechno ostatni bude "selhalo spojeni". Coz, jak rikate "napovi", nicmene se jedna o to, o cem mluvim ja - "zabaleni" vyjimky - firefox Vam nepovi o tom, jaky errorcode vratil "socket", nerekne vam na jake pozici doslo ke korupci paketu. Napise "selhalo spojeni", protoze to je ta "zabalena" chyba/vyjimka. Natoz aby Vam rekl ze jste blbec a ze jste si vykopnul kabel.
Coz souvisi s tim cemu se obcas rika "leaky abstraction" - proste kazda vyssi vrstva je ve vetsine pripadu nucena provest nejake "zobecneni" urcitych chybovych stavu pod jednu "strechu". Pak NENI mozne bez dalsich nastroju zjistit, kde k chybe doslo. To je ale normalni, a je to v poradku. Firefox nema slouzit jako diagnostika site. Na to jsou jine nastroje.

Firefox samozřejmě není diagnostika sítě, ale měl by říct co nejdetailněji, co ví, že se stalo, aby bylo možné to snadno opravit. V tom mají výjimky oproti chybovým kódům velkou výhodu, mohou mít leaky abstraction a zároveň v sobě nést detailní informace (např. reason a backtrace), které jsou pro snadnou a rychlou opravu problémů poměrně zásadní. Nedovedu si moc představit, jak bych u uživatele ladil problémy s Bluetooth, kdybych dostal akorát Could not connect a nevěděl ani, ze které funkce to vypadlo. A ne, uživatel opravdu nedokáže spustit debugger.
Název: Re:C++ a výjimka
Přispěvatel: belzebub 12. 02. 2014, 20:27:54
Ano, jen 3. Projekt se navrhne, napise, odladi, spusti a udrzuje. Klicove slovicko, ktere jste zjevne zamerne ignoroval je "netrivialni". Napiste mi prosim, na kolika netrivialnich projektech (rekneme nad 50 000 radek), v C (ne C++) jste se podilel Vy.

Proč jenom v C? V C jsem napsal asi dva až tři velké projekty, když jsem zjistil, že takhle to dál nejde, a komplet jsem přesedlal na C++, kde mám spoustu projektů. Prostě nebudu se vracet ke starým technologiím, dokud vyloženě nemusím (naposledy při portování Skeldalu na iPhone, naštěstí jsem jen vypomáhal)

Protoze ja mluvim celou dobu o osetrovani chyb pomoci errorkodu v C. VC. Chapu ze ve Vasi snaze me argumety ignorovat jste to pochopil tak, ze za cely zivot jsem se ucastnil 3 projektu vseho vsudy. PROTO jsem psal "v C".
Cela pointa toho odstavce byla ukazat Vam, ze nevite jak delat spolehlive a velke projekty v C.
Jinak pokud Vim, iPhone pouziva ObjC, ne C.
Název: Re:C++ a výjimka
Přispěvatel: Ondřej Novák 12. 02. 2014, 21:38:53
Protoze ja mluvim celou dobu o osetrovani chyb pomoci errorkodu v C. VC. Chapu ze ve Vasi snaze me argumety ignorovat jste to pochopil tak, ze za cely zivot jsem se ucastnil 3 projektu vseho vsudy. PROTO jsem psal "v C".
Cela pointa toho odstavce byla ukazat Vam, ze nevite jak delat spolehlive a velke projekty v C.

Celé je to o tom, že se snažíte mne (a vlastně i sobě) dokázat, že umět čisté C je leet, je to něco co stojí za pozornost. Zatímco já si myslím, že umět jenom C je oslavovat zastaralé technologie a bát se těch nových. Takže v mých očích jste n00b i kdyby jste se naučil v C třeba silanizovat. Je to asi to samé, jako být spisovatel a vyžívat se v tom, jak umíte rychle a bezchybně psát na stroji a díky tomu se můžete řadit mezi elitu... zatímco ostatní píší dávno na počítačích a maximálně se vám vysmějou.

Tím neříkám, že by C mělo být zatraceno úplně. V určitých situacích má své opodstatnění (stejně tak jako ten mechanický psací stroj)....

(Představte si, že dokonce i jednočipy se programují v C++)

Jinak pokud Vim, iPhone pouziva ObjC, ne C.

To je sice možný, ale to jsem já nedělal, nehledě na tom, že celý projekt byl v Marmeládě, nicméně já jsem pracoval s originálními zdrojáky, které jsou v C a upravoval jsem je na konverzi (tu jsem ovšem nedělal).
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Vertigo 12. 02. 2014, 22:06:18
To belzebub
Obdivuji vaši trpělivost.

To "zastaralé technologie"
Řešiče soustavy lineárních rovnic (MSC/Nastran, Boeing) se dodnes píší ve Fortranu...
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 12. 02. 2014, 22:17:01

To "zastaralé technologie"
Řešiče soustavy lineárních rovnic (MSC/Nastran, Boeing) se dodnes píší ve Fortranu...

opakuju:
Tím neříkám, že by C mělo být zatraceno úplně. V určitých situacích má své opodstatnění (stejně tak jako ten mechanický psací stroj)....

Konec flame.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: 死神 13. 02. 2014, 10:00:41
To "zastaralé technologie"
Řešiče soustavy lineárních rovnic (MSC/Nastran, Boeing) se dodnes píší ve Fortranu...

V jakém Fortranu? Třeba tady (http://software.intel.com/sites/products/documentation/hpc/composerxe/en-us/2011Update/fortran/win/lref_for/source_files/rfaf2008k.htm) jsou novinky ve Fortranu 2008. Řekl bych, že to na zastaralé technologie ani nevypadá  ;D
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Pavel Tisnovsky 13. 02. 2014, 11:29:11

To "zastaralé technologie"
Řešiče soustavy lineárních rovnic (MSC/Nastran, Boeing) se dodnes píší ve Fortranu...

opakuju:
Tím neříkám, že by C mělo být zatraceno úplně. V určitých situacích má své opodstatnění (stejně tak jako ten mechanický psací stroj)....

Konec flame.

Přesně, C ani Fortran a (kupodivu :-) ani C++ nepatří mezi tu obrovskou skupinu programovacích jazyků, které umřely, protože nenabídly programátorům nic moc užitečného (těch jazyků už asi budou stovky, úspěšných a používaných maximálně desítky - viz Tiobe).

Já třeba chápu C a Fortran jako velmi dobré a nadčasové DSL a záleží jen na konkrétním projektu, který z těch jazyků se bude hodit víc (ideální je asi spojení dvou či tří úrovní jazyků). Navíc ANSI C je jeden z mála jazyků, u kterých si můžu být jistý, že mé skoro už 25 staré algoritmy ještě za dalších 25 let přeložím a ony pojedou :-) [něco podobného mi říkali lidi o Fortranu 77]

Název: Re:C++ a výjimka v destruktoru
Přispěvatel: td 13. 02. 2014, 11:30:15
To belzebub
Obdivuji vaši trpělivost.

To "zastaralé technologie"
Řešiče soustavy lineárních rovnic (MSC/Nastran, Boeing) se dodnes píší ve Fortranu...

Já zase zcela souhlasím s Ondřejem Novákem, a trpělivost a úsilí které vložil do zdejší diskuze mi přijdou až nadlidské, i když se nezdá že by tu měly šanci padnout na úrodnou půdu. Pro mě jsou řetězené výjimky jednoznačně nejpraktičtější, nejpohodlnější a přitom velice bezpečný způsob, jak v imperativním jazyce ošetřovat chybné vstupy a selhání periferií, a umožnit uživatele přes řetěz výjimek informovat, co neočekávanou situaci způsobilo. V této diskuzi se nicméně zdá, že každý se drží své pravdy, má za sebou N let praxe kdy to dělal po svém, "a proto" je to tak lepší než to druhé řešení.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 13. 02. 2014, 15:38:55
Já zase zcela souhlasím s Ondřejem Novákem, a trpělivost a úsilí které vložil do zdejší diskuze mi přijdou až nadlidské, i když se nezdá že by tu měly šanci padnout na úrodnou půdu. Pro mě jsou řetězené výjimky jednoznačně nejpraktičtější, nejpohodlnější a přitom velice bezpečný způsob, jak v imperativním jazyce ošetřovat chybné vstupy a selhání periferií, a umožnit uživatele přes řetěz výjimek informovat, co neočekávanou situaci způsobilo. V této diskuzi se nicméně zdá, že každý se drží své pravdy, má za sebou N let praxe kdy to dělal po svém, "a proto" je to tak lepší než to druhé řešení.
Tak jednoznačně nejpohodlnější ty zřetězené výjimky opravdu jsou. Není třeba nic analyzovat, promýšlet co všechno volající může potřebovat a chtít, nic takového.

Teď co to znamená dál:

Volající musí přesně vědět, co se uvnitř děje. Pokud netuší, jaké výjimky může v tom zřetězení dostat, tak si s nima moc neporadí. Může leda tak vypsat uživateli what() a doufat že ten bude vědět, co s tím. Mimochodem, what je pro běžného uživatele programu užitečný jen hodně omezeně. Už jen lokalizovat se to dá fakt blbě. Pořádně se to dá akorát zalogovat a poslat vývojáři.

Tím, že volající musí vědět co se uvnitř děje tak efektivně padá veškerá abstrakce. V tu chvíli nejdou prakticky překopat vnitřnosti modulu, protože ty jsou skrz výjimky součástí rozhraní. Program se nedá odstínit od detailů typu "tahám to video ze souboru nebo ze sítě?". Díky těm zřetězeným výjimkám musí každá úroveň programu do které ty výjimky můžou probublat vědět, co přesně se děje níž. Pokud to neví, tak vlastně nemá cenu ty výjimky ani házet.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 13. 02. 2014, 16:09:26
Tím, že volající musí vědět co se uvnitř děje tak efektivně padá veškerá abstrakce.
...
Díky těm zřetězeným výjimkám musí každá úroveň programu do které ty výjimky můžou probublat vědět, co přesně se děje níž. Pokud to neví, tak vlastně nemá cenu ty výjimky ani házet.

To přece není pravda. To je jen tvrzení, které funguje na principu ode zdi ke zdi. Buď mám absolutní abstrakci a nemohu použít výjimky (a to si úplně nejsem jist pravdivostí tohoto tvrzení), nebo se musím vzdát abstrakce úplně. Nic mezi tím nevidíte.

Definice výčtu výjimek patří k definici rozhraní - malá poznámka pod čarou: jako rozhraní si můžete představit plně abstraktní třídu, nebo interface v javě. Každé rozhraní by mělo mít tzv. referenční implementaci, která ukazuje, jak se co má implementovat a mělo by 100% dodržovat pravidla rozhraní. Referenční implementace samozřejmě bude házet jen výjimky z výčtu, žádné jiné. To je ta abstrakce.

Bohužel nebo bohudík, rozhraní vznikají proto, aby skryly implementační detaily, nicméně to stejně nejde, protože už v implementaci nějakého rozhraní můžeme najít výrazné odlišnosti, úlevy a zkratky. Například rozhraní IPes má metodu VrtětOcasem(), tato metoda bude u objektu Buldok prázdná (buldok praktický nemá ocas). A to je ten lepší případ (čekali byste ze metoda IStream::closeOutput způsobí odeslání HTTP requestu pokud je tam HttpStream? No ale logiku to má, i když u TCPStreamu se spíš používá k uzavření spojení - příklad z knihovny LightSpeed).

To samé je to s výjimkami. Každý objekt by měl dodržovat rozhraní i na úrovni výjimek. Ale implementace se může začít potýkat s problémem, který na rozhraní definovaný není. Strojí před problémem. Má to vyhodit jako výjimku, která není na rozhraní definovaná? Nebo to má zabalit do jiné výjimky z nějaké existující, ke které to připojí Reason?

Opět příklad. HttpStream může vyhodit HttpStatusException. Ta ale na rozhraní IStream není definovaná. Tam je definovaná výjimka IOException. (zadání)

 Má se tato výjimka vyhazovat jako IOException a jako reason vložit HttpStatusException?

Opět záleží, jak předpokládáte, že se to bude používat. Předpokládám tuto hierarchii

Kód: [Vybrat]

 + - (1) otevírám HttpStream, chci si něco stáhnout
   + - (2) čtu soubor přes IStream (třeba to je parser JSONu, který pracuje nad obecným streamem)
     + - (3) při čtení obdržím status 500 Internal Server Error

Co chci? Chci na úrovni (2) odchytit IOException? Co s tím na té urovni budu dělat? Pokud je tam JSON parser, tak asi nic, ten to hodí dál. Může to také vyřešit tak, že z toho vybalí reason a ten hodí. Jenže JSON parser nemůže vědět, zda nad ním náhodou není někdo, komu by se třeba hodilo, aby to bylo IOException

Takže asi budu chtít na úrovni (1) odchytávat HttpStatusException.

V tomto případě bych asi pro HttpStream výjimku nebalil a i když není na úrovni rozhraní IStream definovaná. Nicméně mohu předpokládá, že ten, kdo si objednal čtení přes HTTP protokol, bude nejspíš vědět, jak s výjimkou HttpStatusException naložít, spíš než s chybou IOException.


Jak já to dělám? HttpStatusException dědí IOException. Ne vždy to ale takhle jde řešit.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Jakub Galgonek 13. 02. 2014, 16:19:46
Tím, že volající musí vědět co se uvnitř děje tak efektivně padá veškerá abstrakce. V tu chvíli nejdou prakticky překopat vnitřnosti modulu, protože ty jsou skrz výjimky součástí rozhraní. Program se nedá odstínit od detailů typu "tahám to video ze souboru nebo ze sítě?".

Ony i chybové kódy, které může funkce vracet, jsou součástí rozhraní. Pokud překopeš vnitřnosti modulu tak, že najednou z něj budou moci lézt jiné chybové kódy, tak máš také zaděláno na problém.


Díky těm zřetězeným výjimkám musí každá úroveň programu do které ty výjimky můžou probublat vědět, co přesně se děje níž. Pokud to neví, tak vlastně nemá cenu ty výjimky ani házet.

Nemusí vědět přesně, co se děje níž. Volající ví, jaké výjimky mu můžou přijít, a volaný ví, jaké výjimky může vyhodit.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 13. 02. 2014, 16:21:18
Volající musí přesně vědět, co se uvnitř děje. Pokud netuší, jaké výjimky může v tom zřetězení dostat, tak si s nima moc neporadí. Může leda tak vypsat uživateli what() a doufat že ten bude vědět, co s tím. Mimochodem, what je pro běžného uživatele programu užitečný jen hodně omezeně. Už jen lokalizovat se to dá fakt blbě. Pořádně se to dá akorát zalogovat a poslat vývojáři.

Běžně se to dělá tak, že se známé výjimky převedou na chybové hlášky.

Kód: [Vybrat]
void openDocument(const char *fname) {
 try {
    .....
 } catch (FileNotFoundException &e) {
      MessageBox(FormatMessage(IDS_SOUBORNEBYLNALEZEN, e.getFilename());
 } catch (AccessDeniedException &e) {
      MessageBox(FormatMessage(IDS_NEMATEPRISTUPKSOUBORU, e.getFilename());
 } catch (DocumentParseError &e) {
      MessageBox(FormatMessage(IDS_SOUBORNEBYLROZPOZNAN, e.getFilename());
 } catch (std::exeption &e) {
      LogError(e);
      MessageBox("Neznama chyba, kontaktujte vývojáře");
 }

}
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 13. 02. 2014, 17:27:56
Aha, už začínám chápat, kde si nerozumíme. Pokud potřebuju protáhnout nejakou výjimku skrz cizí kód a na obou koncích jsou moje objekty, tak může být reason rozumné řešení. Pokud bych něco takového musel udělat já, tak bych nejspíš ten cizí kód zkusil nějak obejít než přes něj tu chybu protahovat. Např. z callbacku bych chybu dostával ven přes userdata a ne výjimkami a podobně bych postupoval u toho streamu.

Taky moduly skládám dohromady úplně jinak, holt řeším jiné problémy. Třeba se nepotkávám s tak vyžranými jsony, že bych musel parseru předhazovat stream. Json ze serveru přijmu jako string a ten string pak předám parseru. Takhle mám http komunikaci i parser jsonu vedle sebe a ne v kaskádě a nemusím chyby z jednoho modulu protahovat skrz jiný.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Goheeca 13. 02. 2014, 21:20:27
Tím, že volající musí vědět co se uvnitř děje tak efektivně padá veškerá abstrakce. V tu chvíli nejdou prakticky překopat vnitřnosti modulu, protože ty jsou skrz výjimky součástí rozhraní. Program se nedá odstínit od detailů typu "tahám to video ze souboru nebo ze sítě?". Díky těm zřetězeným výjimkám musí každá úroveň programu do které ty výjimky můžou probublat vědět, co přesně se děje níž. Pokud to neví, tak vlastně nemá cenu ty výjimky ani házet.

Ano tohle je celkem nešťastné řešení výjimek v jazycích jako C++, Java. Common Lispové řešení v podobě restartů (http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html) je mnohem lepší.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 14. 02. 2014, 15:24:38
Taková perlička pro Stena a milovníky MSVC (2010)

Kód: [Vybrat]
ParallelExecutor2::~ParallelExecutor2() try {
stopAll();
} catch (...) {
if (std::uncaught_exception()) return;
}

class ParallelExecutor2::Worker {   //error C2911: 'LightSpeed::ParallelExecutor2::Worker' : cannot be declared or defined in the current scope
public:
....
};

Ovšem po úpravě předchozího try catch bloku v destruktoru...

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

class ParallelExecutor2::Worker {
public:
....
};

se to přeloží bez chyby

Evidentně MSVC parser C++ rozhodí zápis s try před tělem funkce. Gcc a v Clangu se to přeloží obojí
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 14. 02. 2014, 21:13:29
Že Visual studio neumí ani tohle mě vlastně ani moc nepřekvapuje. Když porovnám to, co MS zatím jen slibuje s tím, co už GCC a clang mají, tak je to dost děsivé.

Pro ty, co C++ moc neznají doplním, že ty dva kusy kódu ani zdaleka nejsou identické. V prvním bude ten catch block chytat i výjimky, které vyhodí destruktory členských proměnných a bázových tříd toho objektu. Ve druhém případě ten blok chytne jen to, co vyhodí funkce stopAll().
Tuhle konstrukci je možné vidět i v konstruktorech, když je třeba chytit výjimku z konstruktoru bázové třídy.

Předpokládám, že
Kód: [Vybrat]
if (std::uncaught_exception()) return;
je chyba, ano? Takhle by ten catch block spolkl výjimku vždycky a ten test by tam vůbec nemusel být.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Ondřej Novák 14. 02. 2014, 23:04:56
Předpokládám, že
Kód: [Vybrat]
if (std::uncaught_exception()) return;
je chyba, ano? Takhle by ten catch block spolkl výjimku vždycky a ten test by tam vůbec nemusel být.

Není to chyba:
http://stackoverflow.com/questions/5952837/try-catch-block-substituted-for-a-method-block-in-a-destructor

Citace
due to ISO/IEC 14882:2003 15.3 [except.handle] / 16:

    The exception being handled is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. [...]

However it is legal to have a parameterless return in the handler of a function try block for a destructor - it is only forbidden in a function try block for a constructor - and this will supress the rethrow of the exception. So either of these alternatives would prevent the exception from leaving the destructor

Opět ne všechny překladače to umí
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: JSH 14. 02. 2014, 23:23:44
Díky za opravu. Ještě že jsem to zatím nemusel použít. Bohatě mi stačí pasti kolem automatických konverzí, pořadí vedlejších efektů a nedefinovaných operací.
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Curious 24. 02. 2014, 15:55:24
Z jineho soudku, ale podobneho testa - https://www.imperialviolet.org/2014/02/22/applebug.html
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Curious 21. 03. 2014, 09:45:12
Why should I have written ZeroMQ in C, not C++ (part I)

Just to be clear from the very beginning: This is not going to be a Torvalds-ish rant against C++ from the point of view of die-hard C programmer.

I've been using C++ whole my professional career and it's still my language of choice when doing most projects.

Naturally, when I started ZeroMQ project back in 2007, I've opted for C++. The main reasons were:

Library of data structures and algorithms (STL) is part of the language. With C I would have to either depend on a 3rd party library or had to write basic algorithms of my own in 1970's manner.
C++ enforces some basic consistency in the coding style. For example, having the implicit 'this' parameter doesn't allow to pass pointer to the object being worked on using several disparate mechanisms as it often happens to be the case with C projects. Same applies to explicit marking of member variables as private and many other features of the language.
This point is actually a subset of the previous one, but it's worth of explicit mention: Implementing virtual functions in C is pretty complex and tends to be slightly different for each class which makes understanding and managing the code a pain.
And finally: Everybody loves destructors being invoked automatically at the end of the block.

http://250bpm.com/blog:4
Název: Re:C++ a výjimka v destruktoru
Přispěvatel: Mr. Pointless 03. 05. 2014, 03:43:32
http://250bpm.com/blog:4