Portál AbcLinuxu, 3. května 2025 23:57
Výbor pro standardizaci jazyka C++ v rámci ISO se zabývá zařazením API pro 2D kreslení do ISO C++. Svým návrhem se jim zamlouvá API Cairo, i když by bylo nutné doplnit obalení pro C++, jelikož Cairo je v čistém C.
Tiskni
Sdílej:
Kecy, nechtějí přidat Cairo, ale jeho API... to je dost rozdíl.To sice jo, ale je otázka, jak by se k tomu pak případně postavily implementace. Je dost možný, že by prostě akorát přidaly cairo.
libstdc++
.
Pragmaticky si myslím, že C+11 je poslední norma C++, která je pro mě významná.C++14 pridava vicemene drobnosti (ktery ale usnadni psani), a podpora je myslim skoro stejna jako C++11
constexpr
a unicode literals, co vím, ale jinak snad ostatní důležitý věci taky má. V každým případě bych se neomezoval nedostatky MSVC. Přinejhorším máme feature-test macros.
Ostatně je to vidět i z toho, že Linus něco napytlíkoval za 14 dní a byl další vcs na světě.Git rozhodně nebyl na světě za 14 dní. To byla jen nějaká úplně prvotní verze, navíc iirc to byly jen skripty.
Není. A dokonce si myslím, že zdroják není podstatný.Vzhledem k tomu, ze VCS neni na divani a teoretizovani, nybrz na pouzivani, nedava smysl se tim zabyvat dokud obe strany (tedy vy a my ostatni) nebudou mit moznost ziskat stejnou znalostni bazi o teto problematice.
Ohanat sa dokumentaciou je naozaj smiesne. Ak je vas projekt tak dobry ako tvrdite tak urcite sa nejde niekto kto tu dokumentaciu urobi (cez wiki napr.).
Nechci nijak obhajovat systém, který jsem nikdy neviděl, ale pokud sám autor nenapíše ani čárku dokumentace, tak ten systém asi těžko někdo začne používat (obecně). Ten magický uživatel, co by měl napsat tu wiki stránku by měl docela problém pochopit k čemu ten systém je a jak by ho měl používat. Dokumentace by se mu pak psala docela blbě.
Ani bych neřekl, že se Miloslav Ponkrác dokumentací ohání. Prostě jen zmínil co by rád udělal než nějakou svou práci zvěřejní.
Teprve až poté jsem vytvořil repository, a to jako databázi v mysql.Pro zajímavost: jak dlouho trvá porovnání dvou vzdálených verzí na projektu velikosti linuxového jádra?
Vystrčit něco ven a pochlubit se znamená napsat manuál a dokumentaci a trochu to uhladit. Napsat nějaký instalační skript, atd. Plus vytvořit web a propagovat to.Úplně by stačilo stručně popsat formát repozitáře a logiku operací, pokud jsou tak převratné.
Ale však premakaný GC má U++, ale to sa tu už trápne opakujem.Tak to budu muset trapně zopakovat, že U++ žádný GC nemá
Dnes jsem zjistil, že to platí jen tehdy, pokud je ta výjimka toho typu někde zachycována, pokud ne, vaše destruktory se nezavolajíTak jsem to zkousel a v gcc to tak je, v clangu ne. To je teda utra-prasarna co dela gcc... A navic me prijde ze implementovat to taklhe musi byt slozitejsi nez je volat vzdicky...
To je teda utra-prasarna co dela gcc.To co dela clang neni o nic lepsi, tohle v podstate nejde rozumne v C++ resit. Pokud mate neosetrenou vyjimku, nemate jistotu ze muzete vubec destruktory bezpecne zavolat, navic to neresi multithreaded programy, kde muzou vzniknout kaskady vyjimek ci casovani unrollingu, ktere muze byt zpozdene.
to taklhe musi byt slozitejsi nez je volat vzdicky...Je to spise naopak.
To co dela clang neni o nic lepsiclang destruktory vola, tak jak "neni o nic lepsi"?
tohle v podstate nejde rozumne v C++ resitrozumny reseni: zavolam destruktory, ukoncim program
funguje to takhle:to taklhe musi byt slozitejsi nez je volat vzdicky...Je to spise naopak.
clang destruktory vola, tak jak "neni o nic lepsi"?Vam stale nedochazi, ze je to hovadina, ze?
std::terminate()
; to je totiz jedine co muzete bezpecne udelat. Defaultni handler u terminate je std:abort()
, ktery neprovadi zadny cleaning a volani destruktoru, a pokud se vam to nelibi, muzete implementovat svuj handler a nastavit pres std::set_terminate()
, nebo v nejhorsim nastavit std:exit()
.
mohl bys uvest priklad kdy je to nebezpecne?Na hloupu otazku hloupa odpoved: Kdykoliv, kdy to neni jiste bezpecne.
ale tipoval bych to na "moznost optimalizace".Tipujete blbe.
Pokud volani destruktoru pri vyjimce muze neco zpusobit tak je v nem chybaVyjimky se obvykle generuji kdyz dojde k nejake chybe a zpracovani musi limitovat moznost dalsiho selhani.
protoze ke stejnymu problemu dojde pokud ta vyjimka je obslouzena, ale az po zavolani destruktoru.Nemusi, znamou vyjimku jsem schopen osetrit; znovu: znama vyjimka je recoverable error, neznama irrecoverable error a od toho se vse odviji.
#include <iostream> class EEPROM_error: public std::exception {}; class EEPROM { public: ~EEPROM() { std::cout << "causing trouble\n"; } }; int main() { try { EEPROM eeprom; // ... throw EEPROM_error(); // ... } catch (EEPROM_error &e) { std::cout << "recovering\n"; } }vystup bude:
causing trouble recoveringtakze i kdyz vyjimku osetrim, tak ten destruktor me zpusobi problem. A proto je potreba ho opravit. Kdyz ho opravim tak to ze se zavola pri neosetreni vyjimky me nevadi.
Tak ted jste me rozesmal. Napsat tenhle stupidni priklad se znamou vyjimkou na zaklade informace, kterou jsem vam poskytl je skutecne komicke.Ten priklad mel demonstrovat ze pri psani destruktoru musis pocitat ze muze byt zavolany pri jakykoli chybe. Pokud s tim nepocitas tak v nem mas chybu! Protoze pri psani destruktoru nemuzes predvidat jestli dana vyjimka je nekde ve vyssi vrstve znama nebo neznama (osetrena/neosetrena).
Pokud vyjimka nema handler, jedna se neznamou irrecoverable chybu a jedine co jde da delat, je to rychle ukoncit, protoze to muze byt cokoliv.Porad jsem nedostal priklad ktery by tohle odvazny tvrzeni potvrdil. To s tou EEPROM jsem svym ukazkovym kodem vyvratil. Neopak tady v diskuzi byly priklady kdy jsou destruktory uzitecny i pri neosetreny vyjimce.
priklad mel demonstrovat ze pri psani destruktoru musis pocitat ze muze byt zavolany pri jakykoli chybeJak jsou tedy destruktory v C++ připraveny na situace, kdy došla paměť?
myslim ze pokud v nem nic nealokujes tak by nemel byt problemPokud něco volám, tak alokuji na zásobníku, ne?
SIGSEGV
dostanete, jen pokud používáte stack alokovaný linkerem nebo pthready a trefíte se do ochranné stránky. Pokud použijete setcontext
, tak tam žádná ochranná stránka není.
Pokud není dostupná paměť, kterou jste libovolně namapoval, tak dostanete SIGBUS
. Tedy jak kdy. Pokud je to heap na Linuxu, tak ten spustí OOM killer a posílá rovnou SIGKILL
.
ja myslim ze neni jak protoze dopredu se obecne neda spocitat kolik pameti bude potreba.Ale kolik bude destruktor potřebovat místa na stacku se určit přece dá ne? Alokovat něco v destruktoru na heapu je prasárna, to by se nemělo dělat tak jako tak.
Takze v urcitych pripadech by to slo, ale obecne je to problem.Souhlas. Například to lze udělat tak, že zakážeme dynamickou alokaci paměti, rekurzi, volání virtuálních metod a volání funkcí, jenž nesplňují tyto podmínky.
ten destruktor muze volat dalsi funkce (pripadne dalsi destruktory)Volání dalších destruktorů by nemělo vadit – před spuštěním konstruktoru zajistím, že budu mít i místo pro volání destruktoru. Tj. držím dostatek místa, abych mohl najednou zavolat destruktory všech objektů, jenž byly zkonstruovány.
std::bad_alloc
se da osetrit, horsi je kdyz vam treba EMIF controller nahlasi HW chybu SDRAM ci pretece stack.
ze pri psani destruktoru musis pocitat ze muze byt zavolany pri jakykoli chybe. Pokud s tim nepocitas tak v nem mas chybu!Za prve, nikdy nemuzete pocitat se vsim. Za druhe, vyjimky jsou prave mechanismus pro osetreni nekterych chybovych stavu, at jiz v HW ci SW, pokud by tomu tak nebylo, tak je nepotrebujete - pokud nepatrite k tem silencum, kteri v C++ pouzivaji vyjimky ke control flow. Za treti, jsou vyjimky kde recovery je z principu nemozne.
To s tou EEPROM jsem svym ukazkovym kodem vyvratil.Co jste tedy svym skolnim prikladem s vyjimkou majici handler vyvratil?
Za prve, nikdy nemuzete pocitat se vsim.To jestli je mozne udelat bezchybny program je na jinou diskuzi. Kazdopadne pokud se program zacykli protoze nepocital s nejkou chybou, tak je v nem chyba
Co jste tedy svym skolnim prikladem s vyjimkou majici handler vyvratil?Vyvratil jsem, ze toto:
Staci aby vam selhal zapis do EEPROM, vygnerovalo to vyjimku a mit objekt, ktery v destruktoru ceka na dokonceni zapisu ci destruktor ktery pristupuje k protected resource, ktery neni k dispozici, i kdyby to mel byt reference counter.je validni design
Vyvratil jsem, ze toto:Ne. Vas priklad je jen komicka ucelova interpretace me citace, vytrzena z kontextu.Staci aby vam selhal zapis do EEPROM, vygnerovalo to vyjimku a mit objekt, ktery v destruktoru ceka na dokonceni zapisu ci destruktor ktery pristupuje k protected resource, ktery neni k dispozici, i kdyby to mel byt reference counter.je validni design
Ohledne EEPROM, byl to jeden z pripadu, se kterym jsem se setkal. Zapis a cteni seriove pameti se delalo ve vlastnim low priority threadu, kteremu ostatni thready posilaly data. Thread vytvoril object transaction, ktery v konstruktoru inicializovala spojeni, pak se poslaly data a v destruktoru cekalo na dokonceni transakce a uvolneni bufferu. Problem byl v tom, ze behem zapisu dochazelo nahodne v zavislosti na datech diky chybe v i2c prenosu k unaligned access, ktery pres souvisejici interrupt handler generoval vyjimku se kterou nikdo nepocital. Pokud by se volal destructor u teto neosetrene vyjimky, vlakno bude zablokovano, nebot EEPROM thread transakci nikdy nedokonci, a system by nahodne krachnul nekde jinde.Vidím problém zejména v tom, že se v destruktoru čekalo na dokončení transakce. Destruktor nemá na nic čekat, má uklidit po objektu a vypadnout...
Vidím problém zejména v tom, že se v destruktoru čekalo na dokončení transakce. Destruktor nemá na nic čekat, má uklidit po objektu a vypadnout...Souhlas, jenze u velkych projektu s plnem ruznych lidi to neuhlidate. A i u RAII vam to muze jit dohaje, kdyz dealokujete sdilene resources, staci abyste nedostal lock, coz u nestabilniho systemu s kritickou neosetrenou chybou je mozne.
terminate()
a std::abort()
a je to.
std::uncaught_exception
vracelo true
. Kdyby víc programátorů vědělo o téhle funkci, programy v C++ by fungovaly výrazně lépe.
3. Is there any other good use for uncaught_exception? Discuss and draw conclusions.Navic v kontextu multithreaded programu je to fakticky "implementation defined" a dela to psi kusy.
Unfortunately, I do not know of any good and safe use for std::uncaught_exception. My advice: Don't use it.
noexcept
). To, že uncaught_exception
neříká správně, jestli je bezpečné výjimky vyhazovat, nemá na tuhle logiku moc vliv. Pokud se nepovede flush bufferu a někde výš je další výjimka, je úplně jedno, jestli výjimku o neflushnutí bufferu vyhodím nebo ne — někdo ji stejně bude muset někde výš chytit a zamlčet, jinak dostane terminate
. U toho EEPROMu je to podobné, pokud je někde výš výjimka, tak nemá smysl na něj čekat, i kdyby samotný destruktor EEPROMu byl volán ve vnořeném try-catch
; ta vnější výjimka stejně celý proces přerušila.
Pozor také na to, o co zde Sutterovi jde: Sutter zde hlavně argumentuje tím, že destruktor nemá dělat nějakou logiku. S tím souhlasím, destruktor by neměl rozhodovat, jestli udělá commit nebo rollback, případně vůbec zakládat nějaké transakce; buď někdo udělá commit explicitně nebo destruktor automaticky provede rollback (když jsem psal transakce, tak to většinou bylo s runtime_exception
, že se volající nerozhodl). Jenže třeba u případu selhání flushnutí bufferu je špatný nápad výjimku nevyhodit, pokud uncaught_exception
vrátí false
, protože program vesele pokračuje dál, i když se vlastně něco důležitého nepovedlo. A dělat flush jen speciální funkcí zase narušuje RAII.
Implementace, které nemají uncaught_exception
thread-safe, nemají thread-safe ani vyhazování výjimek.
Implementace, které nemají uncaught_exception thread-safe, nemají thread-safe ani vyhazování výjimek.Jak to chcete udelat rozumne thread safe, kdy to vrati
true
teprve az pote, co to udela kompletni stack unwinding vcetne destrukce vsech objektu?
true
dote az to spusti handler vyjimky, std::terminate()
ci std::unexpected()
.
Proste nevim, jak jste dosel k tomu, ze unsafe std::uncaught_exception()
znamena unsafe vyhozeni vyjimky. U prveho musite zajistit pocitani objektu vyjimek [per thread], u druheho ulozeni objektu [per thread], standard navic rika, ze vyjimka nemuze propagovat do jineho threadu a v podstate si vynucuje thread safeness.
Sutterovu pripominku bych videl jinde - pouzitim std::uncaught_exception()
tam vnasite zavislost chovani na skryte [globalni] promenne, coz v kontextu toho, ze se destruktory volaji automaticky i v destruktorech samotnych muze vest k necekanym vysledkum, coz ostatne ukazuje.
Jenže třeba u případu selhání flushnutí bufferu je špatný nápad výjimku nevyhodit, pokud uncaught_exception vrátí false, protože program vesele pokračuje dál, i když se vlastně něco důležitého nepovedlo.A podle vas je vyhozeni vyjimky jedina cesta jak zmenit beh programu?
A dělat flush jen speciální funkcí zase narušuje RAII.Za prve RAII neni modla, za druhe flushnuti neni inicializace/deinicializace resource.
U toho EEPROMu je to podobné, pokud je někde výš výjimka, tak nemá smysl na něj čekat, i kdyby samotný destruktor EEPROMu byl volán ve vnořeném try-catch; ta vnější výjimka stejně celý proces přerušila.Na nic se u selhani nikdy necekalo, jednalo se chybovy stav a tak to slo hned do
std::terminate()
.
Uff, takhle nepsat pozde vecer .... vraci to true dote az to spusti handler vyjimky, std::terminate() ci std::unexpected(). Proste nevim, jak jste dosel k tomu, ze unsafe std::uncaught_exception() znamena unsafe vyhozeni vyjimky. U prveho musite zajistit pocitani objektu vyjimek [per thread], u druheho ulozeni objektu [per thread], standard navic rika, ze vyjimka nemuze propagovat do jineho threadu a v podstate si vynucuje thread safeness.Reagoval jsem na Navic v kontextu multithreaded programu je to fakticky "implementation defined" a dela to psi kusy. Některé C++ knihovny (vzpomínám si na AIX) mají skutečně thread-unsafe výjimky včetně
uncaught_exception
. Že je jejich thread-safeness spojená, je jednoduché: uncaught_exception
vrací true
, pokud existuje nejméně jeden objekt nezachycené výjimky. A ten je buď uložen per thread v TLS, tj. v jiném threadu funguje uncaught_exception
správně, nebo globálně, což evidentně není thread-safe ani pro vyhazování výjimek.
A podle vas je vyhozeni vyjimky jedina cesta jak zmenit beh programu?To by mě zajímalo, jak destruktor může dát programu vědět, že se něco nepovedlo, jinak než výjimkou. Ne, změna globálních stavů á la
errno
do C++ nepatří.
Za prve RAII neni modla, za druhe flushnuti neni inicializace/deinicializace resource.Za prvé RAII je základní kámen bezpečné práce s výjimkami (C++ nemá
finally
) a za druhé je flushnutí bufferu součást deinicializace zdroje, jak ostatně uvidíte třeba v iostream
, které volá fclose
(The fclose() function flushes the stream pointed to by fp (writing any buffered output data using fflush(3)) and closes the underlying file descriptor).
Na nic se u selhani nikdy necekalo, jednalo se chybovy stav a tak to slo hned do std::terminate()
.
Pouze v případě, že tu výjimku nikdo neodchytil.
Reagoval jsem na Navic v kontextu multithreaded programu je to fakticky "implementation defined" a dela to psi kusy.To neznamena, ze
std::unexpected()
je thread unsafe. Nevim jak u AIX, ale u nekterych implementaci se skutecne pocital pocet objektu vyjimek globalne a u pre-C++11 je to korektni reseni. To ma pak nevyhodu, ze to vyzaduje thread safeness destruktoru, i kdyz counter vyjimek je atomicky a std::unexpected()
thread safe.
Ne, změna globálních stavů á la errno do C++ nepatří.Jakakoliv implementace
std::uncaught_exception()
zavadi fakticky globalni stav, navic skryty, implementacne zavisly, tezko pouzitelny bez netransparentnich side efektu. Proti tomu je jasne specifikovana global/thread local promenna lepsi reseni. Tady souhlasim se Sutterem.
(The fclose() function flushes the stream pointed to by fp (writing any buffered output data using fflush(3)) and closes the underlying file descriptor).Tam je podstatny ze se vraci file descriptor, flushnuti je jen tresnicka na dorte.
Pouze v případě, že tu výjimku nikdo neodchytil.Jak jsem psal, u zpracovavanych vyjimek se musi zajistil bezpecny unwinding destruktoru, pak by se to muselo predelat.
Nevim jak u AIX, ale u nekterych implementaci se skutecne pocital pocet objektu vyjimek globalne a u pre-C++11 je to korektni reseniMě to jako korektní řešení teda nepřijde, buď to thread-safe je nebo není. Udělat něco mezi je strašná prasárna. Ale asi to bude splňovat doslovný text C++03, protože ten thready moc neřešil.
Jakakoliv implementace std::uncaught_exception()
zavadi fakticky globalni stav, navic skryty, implementacne zavisly, tezko pouzitelny bez netransparentnich side efektu. Proti tomu je jasne specifikovana global/thread local promenna lepsi reseni. Tady souhlasim se Sutterem.
Ani ne, je zde totiž zásadní rozdíl v zodpovědnosti za ten stav. U std::uncaught_exception
se ptáte jen ve chvíli, kdy ten globální stav využijete, a nic se nestane (nepřijdete o žádnou informaci), pokud se nebudete ptát jindy. U globálního chybového stavu se ale musíte ptát pořád, jinak hrozí, že vám něco unikne a následující operace ten stav opět přepíše; proto C má u všech funkcí, které mohou selhat, návratové hodnoty. Jenže to nejde udělat u destruktoru. Někoho napadlo to implementovat tak, že každá metoda, která by ten stav mohla změnit, jej napřed zkontroluje a vyhodí výjimku, pokud tam je uložena chyba. Tam ale nevidím žádný rozdíl mezi tím a vyhozením výjimky rovnou z destruktoru, navíc to zavádí dost zásadní problém jak řešit situaci, kdy ta druhá metoda měnící ten stav je opět destruktor. A co teprve když je to poslední destruktor při ukončení programu? Program skončí úspěšně a přitom se něco nepovedlo. A aby tohle nebylo málo, tak globální proměnné nefungují správně pro statické inicializace, protože pořadí inicializace mezi jednotlivými kompilačními jednotkami není nijak zaručeno (i když u primitivních typů je zaručeno, že jsou při spuštění programu vynulované).
Sutter by s vámi nesouhlasil: „it is always poor design to allow an operation to report the same error in two different ways“. On totiž takovýhle případ vůbec neřeší a tiše ignoruje.
Tam je podstatny ze se vraci file descriptor, flushnuti je jen tresnicka na dorte.Ano, to je samozřejmě i u destruktoru. Ale když se do té manuálové stránky podíváte, tak uvidíte, že u
fclose
se chyba flushnutí bufferu neignoruje, ale ta funkce vrátí chybu.
Mě to jako korektní řešení teda nepřijde, buď to thread-safe je nebo není. Udělat něco mezi je strašná prasárna. Ale asi to bude splňovat doslovný text C++03, protože ten thready moc neřešil.Jedna vec thread safe funkce, druha je to pouzit thread safe zpusobem, neni problem sprasit predavani vyjimek mezi thready i v C++11.
Ani ne, je zde totiž zásadní rozdíl v zodpovědnosti za ten stav. U std::uncaught_exception se ptáte jen ve chvíli, kdy ten globální stav využijete,Pokud neco nepotrebuji, tak se neptam.
U globálního chybového stavu se ale musíte ptát pořád, jinak hrozí, že vám něco unikne a následující operace ten stav opět přepíše; proto C má u všech funkcí, které mohou selhat, návratové hodnoty.Pokud neco potrebuji, tak se ptam.
A aby tohle nebylo málo, tak globální proměnné nefungují správně pro statické inicializace, protože pořadí inicializace mezi jednotlivými kompilačními jednotkami není nijak zaručeno (i když u primitivních typů je zaručeno, že jsou při spuštění programu vynulované).A vy si neumite poradit s faktem, ze behove prostredi/system se musi inicializovat? Je podle vas problem implementovat a pouzit staticke objekty tak, aby nezarucene poradi inicializace nebyl problem? Psal jste nekdy SW pro bare-metal?
Jedna vec thread safe funkce, druha je to pouzit thread safe zpusobem, neni problem sprasit predavani vyjimek mezi thready i v C++11.V C++11 jde předávat výjimky mezi thready, na to slouží
std::exception_ptr
Pokud neco potrebuji, tak se ptam.Chyby potřebujete hlídat pořád, ne?
Vymezoval jste se vuci uziti globalnich promennych v C++, tohle s tim nesouvisi.No a já psal o změně globálních stavů á la
errno
sloužící pro předávání chyb, ne o globálních proměnných obecně.
A vy si neumite poradit s faktem, ze behove prostredi/system se musi inicializovat? Je podle vas problem implementovat a pouzit staticke objekty tak, aby nezarucene poradi inicializace nebyl problem? Psal jste nekdy SW pro bare-metal?Samozřejmě, existují věci jako nifty counter. No jo, proč to dělat jednoduše, když to jde složitě
%:include <iostream> %:define GIMME_TRY_CATCH struct bla <% char const *msg_; bla(char const *msg): msg_(msg) <% %> ~bla() <% std::cout << msg_ << std::endl; %> %>; int main() %:if defined(GIMME_TRY_CATCH) try %:endif <% bla b("blabla"); throw 5; %> %:if defined(GIMME_TRY_CATCH) catch(...) <% throw; %> %:endif
Pokud zakomentuju ten define GIMME_TRY_CATCH, tak clang 3.3 ani GCC 4.8.2 ten dtor nezavolají, protože vůbec nedojde ke stack-unwindu.A on si nekde sleduje ktery vyjimky jsou zrovna osetreny aby vedel jestli udelat stack unwind? Myslel jsem ze ta soucasna implementace vyjimek nema runtime overhead pokud k vyjimce nedojde?
Jinak já na to nenadávám, jen jsem na to upozornilja na to nadavam...
A on si nekde sleduje ktery vyjimky jsou zrovna osetreny aby vedel jestli udelat stack unwind?Je to soucasti C++ ABI pro danou HW platformu.
Myslel jsem ze ta soucasna implementace vyjimek nema runtime overhead pokud k vyjimce nedojde?Tuším, že se tam generují při kompilaci nějaký "tabulky" pro zachytávání výjimek pro stack unwind, které ukazují do "části" té fce, kde je catch blok
#include <iostream> struct bla { char const *msg_; bla(char const* msg): msg_(msg) {} ~bla() { std::cout << msg_ << std::endl; } }; int maybeThrow() { int a; std::cin >> a; if (a != 0) throw a; return 0; } int main() { bla bla("blabla"); maybeThrow(); return 0; }
0000000000400abd <_Z10maybeThrowv>: 400abd: 55 push rbp 400abe: 48 89 e5 mov rbp,rsp 400ac1: 48 83 ec 10 sub rsp,0x10 ; rax = &a 400ac5: 48 8d 45 fc lea rax,[rbp-0x4] ; rsi = fastcall conv, druhý arg, &a 400ac9: 48 89 c6 mov rsi,rax ; rdi = fastcall conv, první arg, &cin 400acc: bf e0 12 60 00 mov edi,0x6012e0 ; cin.operator>>(a) 400ad1: e8 9a fe ff ff call 400970 <_ZNSirsERi@plt> ; eax = a 400ad6: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] ; if ( (eax - eax) 400ad9: 85 c0 test eax,eax ; == 0 ) { ; goto .__return_zero ; } 400adb: 74 21 je 400afe <_Z10maybeThrowv+0x41> ; edi = fastcall conv, první arg, sizeof(int) 400add: bf 04 00 00 00 mov edi,0x4 ; rax = exception_ptr = allocate_exception(sizeof(int)) 400ae2: e8 79 fe ff ff call 400960 <__cxa_allocate_exception@plt> ; edx = a 400ae7: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] ; *(int*)exception_ptr = edx = a 400aea: 89 10 mov DWORD PTR [rax],edx ; rdx = fastcall conv, třetí arg, 0 400aec: ba 00 00 00 00 mov edx,0x0 ; rsi = fastcall conv, druhý arg, typeid(int) 400af1: be 20 15 60 00 mov esi,0x601520 ; rdi = fastcall conv, první arg, exception_ptr 400af6: 48 89 c7 mov rdi,rax ; throw(exception_ptr, typeid(int), 0) 400af9: e8 82 fe ff ff call 400980 <__cxa_throw@plt> ; .__return_zero: ; return_value = 0 400afe: b8 00 00 00 00 mov eax,0x0 ; goto .__return 400b03: eb 08 jmp 400b0d <_Z10maybeThrowv+0x50> ; .__unwind_handler: ; .__unwind_resume: ; rdi = fastcall conv, první arg = exception_ptr 400b05: 48 89 c7 mov rdi,rax ; unwind_resume(exception_ptr) 400b08: e8 b3 fe ff ff call 4009c0 <_Unwind_Resume@plt> ; .__return: 400b0d: c9 leave 400b0e: c3 ret 0000000000400b0f <main>: 400b0f: 55 push rbp 400b10: 48 89 e5 mov rbp,rsp 400b13: 53 push rbx 400b14: 48 83 ec 18 sub rsp,0x18 ; rax = &bla 400b18: 48 8d 45 e0 lea rax,[rbp-0x20] ; rsi = fastcall conv, druhý arg, "blabla" 400b1c: be 94 0c 40 00 mov esi,0x400c94 ; rdi = fastcall conv, první arg, rax = &bla 400b21: 48 89 c7 mov rdi,rax ; new(&bla) bla("blabla") 400b24: e8 8f 00 00 00 call 400bb8 <_ZN3blaC1EPKc> ; maybeThrow() 400b29: e8 8f ff ff ff call 400abd <_Z10maybeThrowv> ; ebx = return_value = 0 400b2e: bb 00 00 00 00 mov ebx,0x0 ; rax = &bla 400b33: 48 8d 45 e0 lea rax,[rbp-0x20] ; rdi = fastcall conv, první arg, rax = &bla 400b37: 48 89 c7 mov rdi,rax ; bla.~bla(); 400b3a: e8 93 00 00 00 call 400bd2 <_ZN3blaD1Ev> ; eax = ebx = return_value 400b3f: 89 d8 mov eax,ebx ; goto .__return 400b41: eb 1c jmp 400b5f <main+0x50> ; .__unwind_handler rbx = exception_ptr 400b43: 48 89 c3 mov rbx,rax ; rax = &bla 400b46: 48 8d 45 e0 lea rax,[rbp-0x20] ; rdi = fastcall conv, první arg, rax = &bla 400b4a: 48 89 c7 mov rdi,rax ; bla.~bla() 400b4d: e8 80 00 00 00 call 400bd2 <_ZN3blaD1Ev> ; rax = rbx = exception_ptr 400b52: 48 89 d8 mov rax,rbx ; goto __unwind_resume 400b55: eb 00 jmp 400b57 <main+0x48> ; .__unwind_resume: ; rdi = fastcall conv, první arg = exception_ptr 400b57: 48 89 c7 mov rdi,rax ; unwind_resume() 400b5a: e8 61 fe ff ff call 4009c0 <_Unwind_Resume@plt> ; .__return: 400b5f: 48 83 c4 18 add rsp,0x18 400b63: 5b pop rbx 400b64: 5d pop rbp 400b65: c3 retZdá se, že vygeneruje uklízející kód v každé fci (mnou označený jako label .__unwind_handler). Pak bude mít nějakou globální tabulku míst, kde se zachytávají výjimky (tj. neprolejzá asi jednotlivý "stack-framy"), když nic podle toho typeid nenajde, tak se na stack-unwind vykašle a chcípne. Pokud v globlální tabulce najde, tak se zavolají jednotlivé .__unwind_handlery až do místa, kde je catch blok pro danou výjimku a předá se řízení tam.
0000000000400cb0 <_Z10maybeThrowv>: 400cb0: 48 83 ec 18 sub rsp,0x18 400cb4: bf 00 13 60 00 mov edi,0x601300 400cb9: 48 8d 74 24 0c lea rsi,[rsp+0xc] 400cbe: e8 3d fe ff ff call 400b00 <_ZNSirsERi@plt> 400cc3: 8b 44 24 0c mov eax,DWORD PTR [rsp+0xc] 400cc7: 85 c0 test eax,eax 400cc9: 75 07 jne 400cd2 <_Z10maybeThrowv+0x22> 400ccb: 31 c0 xor eax,eax 400ccd: 48 83 c4 18 add rsp,0x18 400cd1: c3 ret 400cd2: bf 04 00 00 00 mov edi,0x4 400cd7: e8 14 fe ff ff call 400af0 <__cxa_allocate_exception@plt> 400cdc: 8b 54 24 0c mov edx,DWORD PTR [rsp+0xc] 400ce0: be 40 15 60 00 mov esi,0x601540 400ce5: 48 89 c7 mov rdi,rax 400ce8: 89 10 mov DWORD PTR [rax],edx 400cea: 31 d2 xor edx,edx 400cec: e8 2f fe ff ff call 400b20 <__cxa_throw@plt> 0000000000400b60 <main>: 400b60: 53 push rbx 400b61: 48 83 ec 10 sub rsp,0x10 400b65: 48 c7 04 24 34 0e 40 mov QWORD PTR [rsp],0x400e34 400b6c: 00 400b6d: e8 3e 01 00 00 call 400cb0 <_Z10maybeThrowv> 400b72: 48 89 e7 mov rdi,rsp 400b75: e8 86 01 00 00 call 400d00 <_ZN3blaD1Ev> 400b7a: 48 83 c4 10 add rsp,0x10 400b7e: 31 c0 xor eax,eax 400b80: 5b pop rbx 400b81: c3 ret 400b82: 48 89 c3 mov rbx,rax 400b85: 48 89 e7 mov rdi,rsp 400b88: e8 73 01 00 00 call 400d00 <_ZN3blaD1Ev> 400b8d: 48 89 df mov rdi,rbx 400b90: e8 ab ff ff ff call 400b40 <_Unwind_Resume@plt>
noexcept
, což je ekvivalent prázdného throw()
. Vysokou režii mělo hlavně specifikování výčtu výjimek u throw()
, protože se nehledaly jen handlery, ale musela se typově kontrolovat i celá cesta k nim.
std::unexpected
std::unexpected
mi prijde k nicemu. Go tam chcete delat, kdyz je to globalni funkce? Zavolat terminate()?
std::unexpected
volá std::terminate
, ale můžete tam zkusit něco jiného, třeba nahradit tu výjimkou jinou ve stylu Javy (std::bad_exception
) Ve výchozím stavu std::unexpected volá std::terminate, ale můžete tam zkusit něco jiného, třeba nahradit tu výjimkou jinou ve stylu Javy (std::bad_exception)Problem je v tom, ze vy v
std::unexpected
nevite kde vam to v programu vzniklo, tak tam s tim temer nemuzete nic rozumneho delat.
std::terminate()
, std::set_terminate()
) a v ni vsechno co neumi uklidit automaticky OS osetrit; ale nepomuze vam to u knihoven.
try {} catch(...) {}
v main()
bude stack unwound.
Celkove je to cele na <>, a u relativne low level veci je lepsi se vyvarovat jednak vyjimek a jednak C++.Taky mám ten dojem. A u relativně highlevel věcí se C++ taky vyhýbám.
Taky mám ten dojem. A u relativně highlevel věcí se C++ taky vyhýbám.Mne spise vadi, ze v rukou [pod]prumernych programatoru (vetsina) je C++ zbran hromadneho niceni, z rady uhlu pohledu turing tarpit.
Čo je zlé na Boost? Teda nie že by som boost používal, naposledy som sa hral pred pár rokmi so serializáciou, ale zdala sa mi to krásna celkom premyslená sada knižníc (alebo skôr šablón?). Zopár programov stavaných na booste použávam (vrátane balíčkovacieho systému) a ani na jeden sa nemôžem v podstate sťažovať.
Je to plné hacků
Nepopieram. Presne v duchu štandardnej knižnice.
zpomaluje kompilaci (výrazně)
Používa šablóny (masívne) a tie sú v GCC pomalé. Nie je to problémom boostu.
error hlášky dělá nepoužitelnými
Videl som ich zopár keď som skúšal. Katastrofa spôsobená kompilátorom / jazykom. Trochu zmierniť to mali koncepty, ale nakoniec sa do štandardu neprijali. Nepovažujem to ale za chybu knižnice.
jako celek je to prostě moc velký balík a bloat
Mnoho knižníc sú len šablóny (tj. vôbec sa nemusí linkovať .so, ale kompilácia trvá dlhšie). Boost je rozsekaný na malé časti, ale je pravda, že ako celok je dosť veľký.
pak taky Boost knihovny, které se sestavují (pár jich je, třeba boost::python) se oficiálně dají sestavit jen přes bjam
Teraz si nie som istý ako je to myslené. Zostavoval som už programy používajúce boost::python pomocou cmake a fungovalo to. Ak ide o zostavenie samotného boost::python nepovažoval by som to v zásade za problém, mnoho knižníc nejde zostaviť bez autotools čo považujem za omnoho väčšie zlo.
rád bych viděl nějaký pořádný systems programming jazyk... zatím Rust je IMHO nejblížProč myslíte? Co se vám tam tak líbí? Typový systém Rustu je momentálně dost slabý na to, aby bezpečně popsal řadu užitečných konstrukcí (například paralelní algoritmy typu rozděl a panuj). V podstatě lze mít jen stromové struktury. Je tam sice počítání referencí a jednoduchý GC, ale to už je snad lepší použít jazyk s plně automatickou správou paměti pomocí pokročilého GC. Další nevýhoda Rustu je množství druhů ukazatelů, což zřejmě povede k duplikaci kódu.
Objects are never accessible after their destructor has been called, so there are no dynamic failures from accessing freed resources. When a task fails, the destructors of all objects in the task are called.
Takže to používá garbage collector (tj. dokud existuje reference, objekt existuje) nebo ty reference jsou weak pointery (tj. když objekt zanikne, ty reference se zneplatní a vyhazují výjimku při přístupu)?Ani jedno. Součástí typu vypůjčených referencí je životnost a staticky se kontroluje, že je menší než životnost referencovaného objektu – když kompilátor tohle nevidí, tak program nepřeloží. Například si vypůjčenou referenci nemůžete uložit do jiného objektu, který by mohl přežít referencovaný objekt.
catch
. Bez vyjimek musim zaneradit vsechny funkce po ceste kodem na obsluhu a propagaci chyby...
Bez vyjimek musim zaneradit vsechny funkce po ceste kodem na obsluhu a propagaci chyby...Nemusite: setjmp() a longjmp(), pripadne POSIX context control vam umozni v rade pripadu bezpecnejsi handling chyb.
If any automatic objects would be destroyed by a thrown exception transferring control to another (destination) point in the program, then a call to longjmp(jbuf, val) at the throw point that transfers control to the same (destination) point has undefined behavior.
Opet vedle, setjmp/longjmp neprovadi stack unwindingTohle jsem původně v diskusi přehlédl.
longjmp
může provádět stack unwinding, pokud to tak je na dané platformě implementováno. Což např. na x64 je ("forced unwinding").
Nemusite: setjmp() a longjmp()setjmp/longjmp je pěkná prasárna. Je to v podstatě jako goto, ale ještě horší
longjmp
, nebo ne, a potom bude longjmp
nádherně leakovat longjmp
mi zajisti potrebnou funkcionalitu a pritom umozni disablovat vyjimky a RTTI.
longjmp
dealokujete data na haldě?
setjmp/longjmp
je transparentnost, presne vim, kam to skoci, jaka cast stacku se ztrati, jsme to schopni i staticky verifikovat. V pripade vyjimek je to diky dynamickemu hledani handleru o poznani slozitejsi, zejmena pokud pouzivate plno third party kodu. Za treti, samotna implementace setjmp/longjmp
je o jednoduzsi, s presneji definovanym chovanim, implementovana v knihovne; byl jsem schopen treba zajistit, ze behem setjmp/longjmp
nedojde k task switch. Zajistit u vyjimek, aby po jejim vzniku nedoslo k task switchi, pred jejim zpracovanim a unwindingem, a dalsim vyjimkam, v jinych threadech, bez souvisejiciho zpozdeni, je casto nemozne, navic implementacne zavisle.
longjmp
pouze posune ukazatel stacku a nastaví signal mask (oboje v user space), klidně se může stát, že během toho dojde k tiku hodin a jádro udělá task switch.
longjmp
volá z user space sigsetmask
pokud nepoužíváte dynamické alokacePouzivame, a nejen alokace SW zdroju, ale i HW zdroju.
tak výjimky asi nepotřebujete, protože ani nemáte RAII,Vyjimky jsou jen dalsi asynchronni event, stejne jako interrupty, takze bezpecnou alokaci resources v asynchronnim prostredi musime resit.
Task switch přeci řeší jádro, ne?Ano. U cele rady OS mohu task switch kontrolovane disablovat, stejne jako treba interrupty.
longjmp pouze posune ukazatel stacku a nastaví signal mask (oboje v user space),
setjmp/longjmp
obnovuje calling environment, funguje i ve freestanding systemu a ukazatel stacku je jen jeden z parametru prostredi.
setjmp/longjmp
je soucasti knihovny, nikoliv implementacne zavisla soucast jazyka a tak ma uzivatel celkem slusnou kontrolu.
Vyjimky jsou jen dalsi asynchronni event, stejne jako interrupty, takze bezpecnou alokaci resources v asynchronnim prostredi musime resit.V tom případě opět nechápu, jakou má výhodu
setjmp
/longjmp
oproti výjimkám, obzvlášť když longjmp
na rozdíl od výjimek nedokáže řešit dealokace.
Ano. U cele rady OS mohu task switch kontrolovane disablovat, stejne jako treba interrupty.Tak to můžete udělat i u výjimek, stačí v konstruktoru výjimky task switch zakázat a v destruktoru (a
std::terminate
, pokud se to nepovolí automaticky při pádu programu) opět povolit.
setjmp
/longjmp
je soucasti knihovny, nikoliv implementacne zavisla soucast jazyka a tak ma uzivatel celkem slusnou kontrolu
setjmp
a longjmp
je podobně implementačně závislé jako vyhazování výjimek, na různých systémech a implementacích to ukládá a obnovuje různé věci. POSIX jenom velmi vágně specifikuje, co se vlastně ukládá a obnovuje. Pokud nahradíte if (setjmp(buf) == 0) { ... } else { ... }
za try { ... } catch (...) { ... }
, longjmp
za throw
a vlastní úpravy v longjmp
a setjmp
přesunete do konstruktoru a destruktoru výjimky, máte to samé a navíc získáte funkční RAII.
V tom případě opět nechápu, jakou má výhodu setjmp/longjmp oproti výjimkám, obzvlášť když longjmp na rozdíl od výjimek nedokáže řešit dealokace.Duvody jsem vam jiz psal.
Tak to můžete udělat i u výjimek, stačí v konstruktoru výjimky task switch zakázat a v destruktoru (a std::terminate, pokud se to nepovolí automaticky při pádu programu) opět povolit.Posledni co bych delal je nahrazoval trivialni a rychle volani funkce citajici maximalne par desitek instrukci vyjimkami. Co se deje mezi zavolanim throw a predanim vyjimky do handleru, pokud existuje, je jedna velka implementacni neznama, od alokace pameti a mozneho vytvareni kopii vyjimky, pres hledani handleru a zpracovani typove informace, stejne jako doba trvani tohoto procesu.
setjmp a longjmp je podobně implementačně závislé jako vyhazování výjimek,Tyto funkce jsou vetsinou velmi primitivni, ukladaji par systemovych registru a provadi par cache operaci, jejich implementace je vesmes nezajimava a chovani celkem jasne specifikovane. Pokud nejsem spokojen s implementaci, mohu tyto funkce nahradit vlastni verzi; u vyjimek komplexni kod generovany kompilatorem proste nezmenite.
Co se deje mezi zavolanim throw a predanim vyjimky do handleru, pokud existuje, je jedna velka implementacni neznama, od alokace pameti a mozneho vytvareni kopii vyjimky, pres hledani handleru a zpracovani typove informace, stejne jako doba trvani tohoto procesu.Co se tam děje, není žádná černá magie, ale je to dobře zdokumentované. Samozřejmě existují implementace, které nepoužívají Itanium ABI, ale ty většinou neumí ani pořádně C++, a tak na nich stejně budete psát prakticky jen v Céčku. Dobu trvání, než dojde k zachycení výjimky, určitě dokážete statickou analýzou spočítat o dost snáze než dobu trvání řetězu
longjmp
ů (předpokládám, že tedy tak řešíte ty dynamické dealokace).
Tyto funkce jsou vetsinou velmi primitivni, ukladaji par systemovych registru a provadi par cache operaci, jejich implementace je vesmes nezajimava a chovani celkem jasne specifikovane. Pokud nejsem spokojen s implementaci, mohu tyto funkce nahradit vlastni verzi; u vyjimek komplexni kod generovany kompilatorem proste nezmenite.Kompilátor toho moc negeneruje. Získá prostor pro výjimku pomocí
__cxa_allocate_exception
(klidně můžete vrátit statický per-thread buffer, který teď používáte pro longjmp
), na získaném místě spustí její konstruktor a nakonec zavolá __cxa_throw
, který si podle té dokumentace celkem snadno můžete napsat vlastní; catch
na konci potom zavolá __cxa_free_exception
, což by u vás byl nejspíš noop. Pokud budete používat výjimky jen jednoho typu (= velmi podobné tomu, co máte teď s longjmp
), tak to bude taky za pár desítek procesorových taktů.
Co se tam děje, není žádná černá magie, ale je to dobře zdokumentované. Samozřejmě existují implementace, které nepoužívají Itanium ABI, ale ty většinou neumí ani pořádně C++, a tak na nich stejně budete psát prakticky jen v Céčku.Za prve, nepisi, ze je to cerna magie, ale implementacni neznama.
které nepoužívají Itanium ABI, ale ty většinou neumí ani pořádně C++Jako treba naprosto zanedbatelny Microsoft C++, kde se prave exception model da [nekompatibilne] zmenit nekolika flagy, a ABI se meni snad kazdou major verzi?
Kompilátor toho moc negeneruje.Generuje toho dosti, casto specificka metadata (minimalne subset RTTI), vcetne jejich parsovani, hledani hlandleru etc. Alokace pameti a kopie vyjimky muze byt provadena, az kdyz je znam handler, a k tomu je dlouha cesta, pokud to primo nezkonci v terminate().
Dobu trvání, než dojde k zachycení výjimky, určitě dokážete statickou analýzou spočítat o dost snáze než dobu trvání řetězu longjmpů (předpokládám, že tedy tak řešíte ty dynamické dealokace).Tak tohle uz je tuplovana kravina. Psal jsem, ze zadnou dynamickou alokaci zdroju ve ztracene casti stacku nemame. Longjump mame jen jeden je ve sve podstate stale jen jump out-of-scope na nejakou predchozi pozici v case a vetsinou se jedna o rychlou, nearly-constant-time operaci. Muzete mi nastinit, jak bychom meli podle vas statickou analyzou spocitat dobu zachyceni asynchronni vyjimky?
Za prve, nepisi, ze je to cerna magie, ale implementacni neznama. Za druhe, o tom co je "poradne C++" rozhoduje konformita s ISO C++ standardem a ta tuhle zalezitost neresi; a rule of thumb je stavet na standardizovanych vecech. Za treti, kazda platforma ma sve specificke prvky C++ ABI - a Itanium je jedna z nich, byt jejich C++ model je ramcove zaklad i pro ostatni. Uvedomte si co to je - binary interface. Mam k tady dispozici tri C++ kompilatory postavene na stejnem EABI (ARM spec) a linkovat je stejne nemuzete, protoze je tam stale vysoky stupen volnosti (kazdy si navic interpretuje standard po svem). Iterface neni implementace, vlastni implemetace se i u stejneho C++ ABI se lisi - a hraje tam roli i OS na kterem to bezi - kapitolou samu pro sebe jsou HW exceptions mapovane jako foreign exceptions - muzete mluvit o stesti pokud za vas resi prave OS - a pokud mozno ne v C++.Všechny tři věci se týkají i
setjmp
/longjmp
. Navíc to Itanium ABI se na ostatních platformách liší minimálně (na které při psaní __cxa_throw
pravděpodobně nenarazíte), na rozdíl od implementace setjmp
/longjmp
.
Jako treba naprosto zanedbatelny Microsoft C++, kde se prave exception model da [nekompatibilne] zmenit nekolika flagy, a ABI se meni snad kazdou major verzi?Microsoft C++ používá jiný systém, ale také plně dokumentovaný. AFAIK se těmi flagy nijak nemění ABI vyhazování výjimek, které také řeší to DLLko. Nakonec ve Windows je také jiná implementace
setjmp
/longjmp
než na UNIXech.
Generuje toho dosti, casto specificka metadata (minimalne subset RTTI), vcetne jejich parsovani, hledani hlandleru etc. Alokace pameti a kopie vyjimky muze byt provadena, az kdyz je znam handler, a k tomu je dlouha cesta, pokud to primo nezkonci v terminate().RTTI nemusíte používat, pokud budete používat jen jeden typ výjimek (tedy jako rychlou náhradu
setjmp
/longjmp
s RAII). Hledání handleru i volání terminate()
řeší __cxa_throw
, kompilátor nic takového negeneruje. Výjimka je ještě před voláním terminate()
vytvořena (konstruktor se volá, ta zkratka při terminate
akorát přeskakuje volání destruktorů), takže v terminate
můžete použít current_exception
a vrátí vám to tu výjimku.
Psal jsem, ze zadnou dynamickou alokaci zdroju ve ztracene casti stacku nemame. Longjump mame jen jeden je ve sve podstate stale jen jump out-of-scope na nejakou predchozi pozici v case a vetsinou se jedna o rychlou, nearly-constant-time operaci.Tak už se rozhodněte, jestli používáte nebo ne. Na tohle jsem totiž napsal Samozřejmě, pokud nepoužíváte dynamické alokace (a vůbec alokace zdrojů), tak výjimky asi nepotřebujete, protože ani nemáte RAII, které je na nich závislé, na což se mi dostalo odpovědi Pouzivame, a nejen alokace SW zdroju, ale i HW zdroju.
Všechny tři věci se týkají i setjmp/longjmp. Navíc to Itanium ABI se na ostatních platformách liší minimálně (na které při psaní __cxa_throw pravděpodobně nenarazíte), na rozdíl od implementace setjmp/longjmp.Ne. Chovani
setjmp/longjmp
je dano C/C++ standardem. Implementace je v danem kontextu nezajimava a vesmes velmi primitivni (ukladaji/obnovuji se v podstate jen systemove registry ve spravnem poradi) a verejne knihovni funkce, narozdil od internich, lze vetsinou reimplementovat bez vetsich rizik.
Microsoft C++ používá jiný systém, ale také plně dokumentovaný.Bullshit. Na WinCE/SH4 jsme museli podepsat NDA, abychom ji dostali a lide od Trolltech/Qt ji tehdy nedostali vubec. Pokud je to oficialne verejne dostupne, sem s tim.
RTTI nemusíte používat, pokud budete používat jen jeden typ výjimekA jak jako presvedcim genericky kompilator (a) pouzivat jeden typ vyjimek (b) negenerovat pro ne nejakou obdobu RTTI?
Hledání handleru i volání terminate() řeší __cxa_throw, kompilátor nic takového negeneruje. Výjimka je ještě před voláním terminate() vytvořena (konstruktor se volá, ta zkratka při terminate akorát přeskakuje volání destruktorů), takže v terminate můžete použít current_exception a vrátí vám to tu výjimku.Zacinate pouzivat pristup, kdy reagojete na neco co jsem bud nerikal, nerozporoval nebo neresil. Kompilator muze generovat jednak kod a jednak specificke informace; kod MS VC pouziva oboji - compiler-generated unwind funclety a specialni datove struktury, a i dalsi kompilatory pouzivaji minimalne specialni data pro unwind personality funkce.
Tak už se rozhodněte, jestli používáte nebo ne. Na tohle jsem totiž napsal Samozřejmě, pokud nepoužíváte dynamické alokace (a vůbec alokace zdrojů), tak výjimky asi nepotřebujete, protože ani nemáte RAII, které je na nich závislé, na což se mi dostalo odpovědi Pouzivame, a nejen alokace SW zdroju, ale i HW zdroju.Psal jsem "zadnou dynamickou alokaci zdroju ve ztracene casti stacku nemame". I kdybychom meli, muze to byt irelevantni, pokud bychom meli jiny mechanismus jak trackovat dynamicky alokovane zdroje (e.g. resource manager).
Bullshit. Na WinCE/SH4 jsme museli podepsat NDA, ...Fajn, někde je implementace nedokumentovaná. Ale chování C++ výjimek je stejné, podle specifikace jazyka.
Jako treba naprosto zanedbatelny Microsoft C++, kde se prave exception model da [nekompatibilne] zmenit nekolika flagy, a ABI se meni snad kazdou major verzi?Chování C++ výjimek a ABI je stejné, mění se jen chování Windowsích SEH výjimek. Nové verze Visual C++ přichází s dodělanější podporou C++ (v tom MS laguje), což může změnit ABI někde, ne nutně u výjimek nebo někde kde by to vadilo. Naposledy C++ do std::list<> přidalo .size() v O(1), což změnilo layout té struktury, to technicky vzato mění ABI.
A jak jako presvedcim genericky kompilator (a) pouzivat jeden typ vyjimek (b) negenerovat pro ne nejakou obdobu RTTI?Generuje pár desítek bajtů pro každý házený typ, tak málo skoro nikdy nevadí, asi ani na embedded systémech.
Vase snaha obhajit pouziti vyjimek i za cenu zabihani do z hlediska C++ nestandardnich implementacnich detailu, internich funkcich zacinajicich "__" - ...Uživatel Sten by jména funkcí nemusel zmiňovat, jsou dobrá akorát pro dohledatelnost a porozumění.
Mam k tady dispozici tri C++ kompilatory postavene na stejnem EABI (ARM spec) a linkovat je stejne nemuzete, protoze je tam stale vysoky stupen volnosti (kazdy si navic interpretuje standard po svem).Fajn, autoři překladačů se neshodli na dostatečně definovaném ABI. Ale to není problém jazyka. Každý jazyk (i ručně dělaná knihovna C++ výjimky, i knihovna na setjmp/longjmp), který zajišťuje volání destruktorů a hlášení detailů chyby jinými typy než int-em bude řešit stejné problémy. Když nepoužijete destruktory, přesunete složitost/práci na jiné místo a na jinou formu, např na nějaké resource managery, clan-up stack jako v Symbianu. Když odeberete hlášení detailů z místa selhání, můžete dávat uživateli akorát hlášky typu "operace, které nerozumíte, selhala, protože neexistuje soubor s (nyní) neznámým jménem".
... kapitolou samu pro sebe jsou HW exceptions mapovane jako foreign exceptions ...HW trapy jsou tzv. asynchronní výjimky, mohou nastat (z pohledu překladače) u každé instrukce a C++ s nimi proto nepočítá. Je stejně těžké je řešit i v C. Jestli dobře rozumím Vaší situaci, tak volání destruktorů ani házení více typů nepoužíváte, a bojíte se i malého množství překladačova cleanup kódu (tipuji 8 bajtů na funkci nemající cleanup) a divokého kódu v destruktorech které by někdo mohl napsat. V takovém případě překladání v C nebo s vypnutými výjimkami zajistí, že se to nestane - jinak dodržování vašeho specifika závisí na disciplíně nebo lint-like nástrojích. To musí jít velice embedded systém; tipuji správně 16bit? Ale to není problém C++ výjimek ve většinovém případě. Ve většinovém případě vadí jen to, že programátoři neoznačují funkce, které mohou selhat.
In an effort to reduce code and executable size, LLVM does not use RTTI (e.g. dynamic_cast<>;) or exceptions. These two language features violate the general C++ principle of “you only pay for what you use”, causing executable bloat even if exceptions are never used in the code base, or if RTTI is never used for a class. Because of this, we turn them off globally in the code.A to maji stesti, ze jejich SW beha na plnohodnotnym OS, omeznem poctu kompilatoru a nejedna se systemove programovani, potom by meli horsi problemy velikost kodu a binarek.
longjmp
je, že musíš mít někde schovanej jeden nebo několik jmp_buf
ů a seš omezen na jedinej typ 'výjimky' - int
.
Mimochodem, RTTI na výjimky nepotřebuješ, může klidně vypnout RTTI a přitom používat výjimky.
Hmm, chci vidět, jak tohle uhlídáš, když neuhlídáš, aby se nečekalo v dtorech.Fair enough. To ze to neni dobre reseni jsme vedeli, ale kod fungoval, nebyl to nikdy problem, navic to byla cast dodana zakaznikem. Za techto podminek nema delat refaktoring smysl, naklady a riziko rozbiti funkcniho kodu pak neobhajim. Pokud je to nutnost, uhlidame to.
Mimochodem, RTTI na výjimky nepotřebuješ, může klidně vypnout RTTI a přitom používat výjimky.Vyjimky potrebuji urcity subset RTTI ci nejakou introspeci a je na implementaci, zdali vam to dovoli ci ne. Starsi ARM kompilatory to potrebovaly a navic tam bylo plno limitu (staticke linkovani, zaviselo na pouzite std lib etc.); vse je to implementacne zavisle.
-fno-rtti
Disable generation of information about every class with virtual functions for use by the C++ run-time type identification features (dynamic_cast and typeid). If you don't use those parts of the language, you can save some space by using this flag. Note that exception handling uses the same information, but G++ generates it as needed. The dynamic_cast operator can still be used for casts that do not require run-time type information, i.e. casts to "void *" or to unambiguous base classes.
Nevim jak je to u Clangu/LLVM, ale ti mají celý nějaký vlastní rtti systém (prý efektivnější)...
MSVC zase používá jakousi windowsí systémovou implementaci exceptions určenou pro všechny možný jazyky, takže počítám, že na C++ rtti to taky nebude závislé.
Imho tuhle závislost budou mít jen staré/obskurní implementace... imho.
catch
block zachytava vyjimku v podstate potrebujes udelat dynamic_cast
.
takové hluboké error struktury by vůbec neměly existovat...- neobhajitelny dogma
a pak v higher level jazycích, kde už ale mám k dispozici pořádný error handlingCo je timhle mysleno? Docela by me zajimalo jestli existujou dalsi zpusoby pro error handling krome error kodu a exceptions. Ted me zadny nenapadaj...
je to peklo kvůli interakci s exception-unsafe knihovnamitohle vadi jenom pokud ta knihovna vola nejaky tvuj callback.
a pak taky věcem popsaným třeba tady: http://yosefk.com/c++fqa/exceptions.htmlTo ze bys nemel volat vyjimky z konstruktoru/destruktoru povazuju jenom za drobnost na kterou je potreba si dat pozor. Asi bych to nenazval peklo
třeba jazyky s podporou pro option types a pattern matchingemto jsou v podstate variace na error kody ne?
v nějakých memeber smart pointerechPlna konstrukce memberu neni garantovana, nemuze.
std::terminate
, kam si můžete pověsit svůj handler, pokud potřebujete uklízet nějaké sdílené zdroje (všechny nesdílené zdroje uklidí operační systém)
Plna konstrukce memberu neni garantovana, nemuze.Kde? V těle konstruktoru jo.
class bla { bla() try: member1(...), member2(...), member3(...) { // ... } catch (...) { // ... } };
const char*
místo std::string
a mají naprosto šílený buffering, Boost s tím třeba problémy nemá) a ani z destruktorů, pokud víte, jak na to.
Co je timhle mysleno? Docela by me zajimalo jestli existujou dalsi zpusoby pro error handling krome error kodu a exceptions. Ted me zadny nenapadaj...Dá se vymyslet ledacos, dokonce i nad obyčejným Céčkem. Docela zajímavá je třeba transakční sémantika: modifikace globálního stavu se logují a v případě chyby se může podle logu provést rollback. Na používání je to velmi příjemné a při šikovné implementaci to může mít minimální režii: například pokud alokujete paměť primárně z memory poolů, stačí zalogovat počáteční stav poolu. Před časem jsem si s tím trochu hrál, nabízím (zatím mírně pokusnou) implementaci v LibUCW.
U C++ je lepší se vyhýbat exceptions kdykoliv - stojí to za h*vno... třeba parametr -fno-exceptions gcc/clangu s tímto pomůže.To je možné, ale bez výjimek to imho kolikrát stojí z h***o ještě víc...
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.