Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: ondra.novacisko.cz 30. 10. 2010, 22:41:04
-
Schválně, zda přijdete na to, co je na tomhle kódu špatně a proč si dotyčný programátor zaslouží vytahat za uši.
if (&(builder.getUnMarshaledData()) == 0)
throw Fault_t(builder.getUnMarshaledErrorNumber(),
builder.getUnMarshaledErrorMessage());
Všechny informace tam jsou, netřeba se ptát na obsah jednotlivých funkcí. ;D
-
C++ moc neznam, ale hadam, ze to bude memory leak po vytvoreni te vyjimky? Nejspis zlozvyk z Javy.
-
Dáme prostor ještě dalším čtenářům. Pokud někdo víte a nechcete to prozradit, tak mi aspoň napište, co byste s takovým kolegou, nebo podřízeným udělali a jak moc velký problém to podle vás je
-
Ukazatel na vysledek funkce? Programator nechape vnitrni procesy C/C++, obrovsky problem/dopad v komercnim prostredi.
-
hs ma pravdu. Taky mi to bylo divne, ta adresa. To co jsem rikal by platilo jen kdyz by tam bylo new, takhle se to zrejme alokuje na stacku.
-
Kdyby getUnMarshaledData() bylo makro, nemuselo by se jednat o adresu na lvalue :D
Za prasárnu považuji "== 0"; mělo by být "== NULL".
-
NULL je C prasarna. v C++ by sa malo pisat 0.
-
1) když chce programátor používat exception.. proč tam píše podmínku... zvlášť když...
2) ..ta metoda zřejmě vrací referenci, takže z ní dělat ošklivý pointer je nesmysl..
3) tu exception by měla vyhodit už ta metoda
-
1) když chce programátor používat exception.. proč tam píše podmínku... zvlášť když...
3) tu exception by měla vyhodit už ta metoda
Nemusí to být jeho metoda, popř. ta metoda se může chtít používat jinde bez výjimky, popř.
se typ výjimky může lišit dle použití - sice ne úplně standardní techniky, ale za určitých podmínek by mohli být opodstatněné. Ale máš pravdu, že spíš je to prasečina.
2) ..ta metoda zřejmě vrací referenci, takže z ní dělat ošklivý pointer je nesmysl..
No jsou dvě možnosti - buďto metoda může vrátit "neplatnej objekt" (NULL) - pak je ale totální prasárna (že to musim napsat takhle) aby vracela referenci. Nebo nemůže vrátit NULL (ať už nevrací referenci nebo vrací ale nevrátí nikdy NULL) a pak nemá podmínka smysl.
Poslední možnost, která mě napadá, je nějaké obskurní přetížení operátoru &, ale to je nejmíň taková prasečina, jako předchozí případy.
Jinak co se týče sporu NULL a 0, imho je jedno co se používá - evidentně lze odůvodnit obé, mělo by to být ale používané konzistentně. Osobně mám radši ==0, je to kratší :-).
A to tu ještě nezazněla možnost !a :-)
-
Onda.Novacisko:
Co bych mu udelal? To zavisi od toho jaky ma plat. Pokud je to nejaky "pocitacovy delnik" a ma mene nez ~40-45tis./mesicne, tak bych ho jen upozornil a problem mu vysvetlil. Pokud ma vice, asi bych mu strhnul nejake premie nebo neco v tomto smyslu a samozrejme ho upozornil a problem vysvetlil.
-
Především bych řekl, že adresa v paměti nemůže být nikdy 0, takže ta podmínka bude vždycky FALSE.
Je otázka, jestli to bylo od programátora přehlédnutí, nebo je to "systémová chyba". Chyby děláme všichni, že .. :)
-
Neni nahodou ten test na adresu navratove hodnoty (vychazim predpokladu, ze funkce vraci hodnotu ve stacku, jinak nechapu proc by musel autor ziskavat adresu navratove hodnoty) absolutne zbytecny a nikdy neselze? Mam takovy dojem, ze statickym hodnotam se prizuje adresa vzdy a kdyz dojde pamet ... to uz v takovem pripade resi rovnou mechanizmy v systemu. Pokud se tedy nepletu, k vyjimce nikdy nedojde....
-
Především bych řekl, že adresa v paměti nemůže být nikdy 0
Zjevně offtopic k daném příkladu, ale kde si myslíš, že je po startu x86 uložená adresa handleru dělení nulou? ;)
-
Správně odpověděli ti, co poznali, že funkce getUnMarshaledData() vrací referenci ale za určitých okolností může vrátit referenci na adresu 0. V tom je ta prasárna. Nejhorší je, že tohle pochází od lidí (konkrétního autora neznám), kteří nepatří mezi nováčky a rozhodně znají jak se které konstrukce v C++ překládají. A v tom je asi ten největší průser, protože to napíšou tak, jak je to v danou chvíli nejjednodušší, dyť, stejně to bude fungovat, reference je přece pointer.
Uvedený příklad samozřejmě funguje jak má, tedy funkce opravdu může vrátit NULL-referenci a podmínka zafunguje a vyhodí výjimku
Správně by byly dvě možnosti. První možnost předpokládá, že upravíme rozhraní tak, aby funkce vracela, místo reference, ukazatel. Vůbec sám zastávám pravidlo, že pokud nějaká funkce vrací ukazatel, nebo předává parametry jako ukazatel, musí vždycky druhá strana provést test na NULL (0 - v C++ sice oficiálně NULL není, přesto ho používám kvůli čitelnosti). Pokud se něco předává nebo vrací jako reference, nesmí se předat NULL, tedy vždy musí referencovat platný objekt. Zodpovědnost za správnost dat nese strana, která parametry nebo výsledek poskytuje.
Pokud by nebyla možná změna v rozhraní (například proto, že rozhraní je poděděné), druhým řešením je výjimka uvnitř funkce getUnmarshaledData().
Toho člověka samozřejmě potrestat nemohu, ale zajímal mě jen názor :-)
-
A ještě pro zajímavost, jak vypadá funkce getUnMarshaledData()
Value &Builder::getUnMarshaledData() {
return *value; //value je member promenna objektu typu Value *
//vsimnete si dereference NULL pointeru
}
-
Především bych řekl, že adresa v paměti nemůže být nikdy 0
Zjevně offtopic k daném příkladu, ale kde si myslíš, že je po startu x86 uložená adresa handleru dělení nulou? ;)
Řekl bych že někde jinde, než kde mohou být uloženy data.
A jen tak pro zajímavost, kde je specifikováno, že &NULL == 0? Resp. co znamená *NULL? Jestli to funguje, tak podle mne jen náhodou (né že bych byl odborník na C++, třeba se pletu :).
-
Nešlo by to přeložit do pascalu nebo assembleru x86 pro ty co neumí C+-x:
-
A jen tak pro zajímavost, kde je specifikováno, že &NULL == 0? Resp. co znamená *NULL? Jestli to funguje, tak podle mne jen náhodou (né že bych byl odborník na C++, třeba se pletu :).
NULL je v C++ :
#define NULL 0
-
Možná kecam, ale z hlavy bych řek, že NULL je definovaný jako
#DEFINE NULL 0
Z čehož vyplývá, že věci typu *NULL nebo &NULL je nesmysl.
Tak jsem se kouk. V souboru sys/_null.h (includovaný ze stdio.h) je definice:
#ifndef NULL
#if defined(_KERNEL) || !defined(__cplusplus)
#define NULL ((void *)0)
#else
#if defined(__LP64__)
#define NULL (0L)
#else
#define NULL 0
#endif /* __LP64__ */
#endif /* _KERNEL || !__cplusplus */
#endif
Takže jsem se skoro trefil. Až na to, že *NULL vlastně smysl alespoň teoreticky má - něco na adrese 0 bude. Používat to bych ale nedoporučoval...
EDIT: tak oprava, v C++ to opravdu smysl nemá, tam 0 není přetypovaná na void.
D.A. Tiger: V hlavičkových souborech je zcela určitě, jde o to, jestli je v normě...
-
NULL je v C++ :
#define NULL 0
NULL je ukazatel, takže je to
#define nULL (void *) NULL
*NULL označuje tedy hodnotu na adrese 0, tedy nějaké číslo (může a nemusí to být nula, to je asi fuk).
Když si definuji Typ *p = NULL, potom &p == *nejaka adresa v pameti, zarucene nenulova*
Co ale udělá &NULL, ke kterému dojde v příkladu (jestli se nepletu)? Kde je to definováno?
-
Samozřejmě je to #define NULL ((void *)0) :o
-
Především bych řekl, že adresa v paměti nemůže být nikdy 0
Zjevně offtopic k daném příkladu, ale kde si myslíš, že je po startu x86 uložená adresa handleru dělení nulou? ;)
Řekl bych že někde jinde, než kde mohou být uloženy data.
A jen tak pro zajímavost, kde je specifikováno, že &NULL == 0? Resp. co znamená *NULL? Jestli to funguje, tak podle mne jen náhodou (né že bych byl odborník na C++, třeba se pletu :).
Zmíněná adresa handleru je uložená právě na adrese nula (dokud se nezmění IDTR). x86ce brání ve vykonávání kódu od prvního bytu chybějící příznak Execute, popř. příliš vysoká hodnota CPL.
M$:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
-
Samozřejmě je to #define NULL ((void *)0) :o
Ano ale v C. V C++ se tato konstrukce jako NULL pouzivat nesmi.
-
Možná kecam, ale z hlavy bych řek, že NULL je definovaný jako
#DEFINE NULL 0
Z čehož vyplývá, že věci typu *NULL nebo &NULL je nesmysl...
Ano, je videt, ze si rozumime :-)
D.A. Tiger: V hlavičkových souborech je zcela určitě, jde o to, jestli je v normě...
Hlavu za to nedam, ale rekl bych ze norma minimalne zakazuje starou C definici...
-
Tak máš pravdu, C++ standard definuje NULL, a to jako typ integer 0.
18.1.3
The macro NULL is an implementation-defined C++ null pointer constant in this International Standard (4.10). *188)
188) Possible definitions include 0 and 0L, but not (void*)0.
4.10.1
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (4.4).
-
Referencovat NULL nejde, ale jde dereferencovat pointer s hodnotou NULL. Pokud udělám
int *a = 0;
int b = *a;
tak mě to spadne na SIGSEG
Pokud ale napíšu
int *a = 0;
int &b = *a;
Pak program nespatne, ale vytvořím referenci na proměnnou int na adrese 0. On rozdíl mezi referencí a pointer není žádný, jde jen o syntaxtický zápis. Ale má určitý význam, a o tom to je.
Jinak NULL v C++ opravdu není, přesto jej radši používám, protože se to líp čte. Ještě raději používám
enum NilType {
nil = 0
}
A přiřazuji nil. Tohle je zajímavý u chytrých ukazatelů, které by jinak bylo nutné konstrukovat s parametrem int pro vytvoření neininicalizovaného chytrého ukazatele. Napsat konstruktor reagující na NilType je rozhodně lepší.
Zajímavý je, že s NULL i s nulou má C++ obecně docela problémy, například při instanciování šablon funkcí. Proto se myslím C++0x plánuje typovaný NULL. Čili zpět od obecné nuly k nějakému speciálnímu typu.
-
Referencovat NULL nejde, ale jde dereferencovat pointer s hodnotou NULL.
Což není divu, když pointer s hodnotou NULL má právě jinej typ než NULL samotný.
Což je IMHO právě záměr standardu - aby se alespoň částečně zamezilo nechtěnému dereferencování NULL.
Řešení s enum pro šablony je ale hezkej příklad C++ kouzelnictví.
-
Zajímavý je, že s NULL i s nulou má C++ obecně docela problémy, například při instanciování šablon funkcí. Proto se myslím C++0x plánuje typovaný NULL. Čili zpět od obecné nuly k nějakému speciálnímu typu.
No treba knihovna Loki, definujici sablony pro genericke programovani (hlavne) knihoven, ktere zachazeji s typy ( trida, struktura) jako s objekty samotnymi, uz pomerne dlouho takovy jednoduchy typ pouziva. Neco ve stylu
struct nil_type { };
Vyuziva to treba v typovych seznamech, atpd.... Ale netusim, jak by se to dalo pouzit tak jako Makro NULL. To neni ukazatel, ale prece jen cislo adresy, ktere slouzi pouze k testovacim ucelum, zadna pouzitelna data na ni nikdy nebudou...
-
Tak uz jsem tu ztrapnil, ale nemohl se ten programator pokusit predat nulovy pointer z te funkce, ktera je definovana ze vraci odkaz, ktery, pokud se nepletu, narozdil od pointeru, nikdy NULL byt nemuze?
Nerikam, ze to schvaluji. Ale neni mozne, ze tim potreboval obejit nejaky problem - treba omezeni dane nejakym rozhranim?
-
Nerikam, ze to schvaluji. Ale neni mozne, ze tim potreboval obejit nejaky problem - treba omezeni dane nejakym rozhranim?
To je samozřejmě možné, ale už z principu u rozhraní nevíme, kdo je na druhé straně. A pokud ten na druhé straně vidí referenci, může být dost překvapen, když ta reference je nulová. V tom rozhraní to určitě má smysl. A pokud nám to z nějakého důvodu nevyhovuje, můžeme
- použít jiné rozhraní
- vrátit referenci na proměnnou se speciální hodnotou (která pro mě znamená chybu)
- vrátit referenci na proměnnou s bezpečnou hodnotou a objekt nastavit do chybového stavu (zjistitelný přes jiné rozhraní)
- vyhodit výjimku - což je běžný postup, třeba dynamic_cast na referenci taky vyhazuje výjimku, pokud se převod nezdaří. nemůže vrátit nulovou referenci
PS: Jiným rozhraním se myslí extra rozhraní, který konkrétní implementace navíc dědí a do kterého se dostaneme dynamic castováním původního rozhraní na nové rozhraní.
-
Pokud ale napíšu
int *a = 0;
int &b = *a;
Pak program nespatne, ale vytvořím referenci na proměnnou int na adrese 0. On rozdíl mezi referencí a pointer není žádný, jde jen o syntaxtický zápis. Ale má určitý význam, a o tom to je.
Aha, konečně mi to došlo :) Měl bych si zopakovat reference ::)
-
Ale no tak, to by se prece mohlo stat kazdemu. Nema cenu z toho delat vedu. Co to fixnout treba takhle:
if(!builder.getUnMarshaledData())
Zadne NULL viz. Virius: Pasti propasti C++
ani ==0, vedouci na zbytecnou komparaci v kodu.
Kdyz trvate na porovnani, zacnete pouzivat sablonove nullptr s existujicim konverznim operatorem pouze na ukazatel http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf),
nebo si pockejte az ho bude gcc 4.6 umet nativne jako soucasti C++0x.
Poke aka pety.
-
Nepochopil :-D
1)
if(!builder.getUnMarshaledData())
Nemá smysl. Ledaže by teda ta třída měla definován operátor !, ale to by to zas spadlo.
2) Je úplně jedno, jestli == null, ==0 nebo !, vzhledem k tomu, že je to smyslově ekvivalentní, tak je to čistě akademická debata (jediný o něco lepší řešení je ten nullptr).
3) Zopakuj si, co je to reference a jak se má používat. Pokud Ti to přijde jako normální, pracovat na jednom kódu s Tebou bych fakt nechtěl. :o
-
Ver ze pochopil. :-D
Je v obecne slusnosti definovat operator!. viz cele STL.
Prave to spadnuti je spravne, na chybu se prijde hned. Nejhorsi jsou chyby ktere se projevi az pozdeji.
A vsimnete si, ze jsem nikoho v prispevku nenapadl, ani se vytahoval, viz. egoless programming.
Poke aka pety.
-
Ale no tak, to by se prece mohlo stat kazdemu. Nema cenu z toho delat vedu. Co to fixnout treba takhle:
if(!builder.getUnMarshaledData())
Zadne NULL viz. Virius: Pasti propasti C++
ani ==0, vedouci na zbytecnou komparaci v kodu.
pety.
¨
Tak asi by to šlo, kdyby ten operator! byl definován takto
bool Value::operator !() const {return this == 0;} //nebo !this
On je problém ten, že funkce vrací referenci a strčit před to vykřičník znamená volání operatoru! nad výsledkem. Kdyby vracela ukazatel, tak by to vaše samozřejmě prošlo. Jinak testovat v metodě objektu this na nulu je také dobrá prasárna, přesto se k tomu někdy také uchyluji, zvlašť tam, kde je zvýšený riziko toho, že tak nastane. Lze to ale jen u nevirtuálních metod. Třeba při procházení stromu, když jsem línej místo testu na 0 u každého ukazatel na podstrom testovat this na nulu, protože to pak umožňuje mít root stromu roven nule a před procházením už netestovat stav tohoto ukazatele (prázdný strom). Ale jinak nedoporučuju.
Co se samotného testu na 0 nebo na NULL, tak viděl jste někdy výsledný kód?
or ecx, ecx
je IsNull
: ....
IsNull: .....
-
Jenže evidentně ten kdo to "spáchal", tak nechtěl, aby to spadlo, ale chtěl zneužít vrácení referencí.
Ono to co napsal není chyba ve smyslu, že by to dělalo něco jiného, než by chtěl, nebo že by to padalo. Ona je to "jen" prasečina.
Co se týče operátoru !, tak tam mě předběh ondra. Jo, šlo by to tak, ale je to prasečina. A navíc - u některejch objektů má ! smysl (ale pouze u některejch) a tam by se to tlouklo významem.
. A hlavně, mimo spešl hodně dobře odůvodněnejch a okomentovanejch případů by nikdy nemělo nastat, že this==0, takže smysl toho operátoru nechápu. I v tom procházení stromu je imho daleko lepší varianta mít strom s hlavou.
A už vůbec nevím, kdes vzal, že v STL je všude definován operátor not. Co vím, tak kontejnery ho právě nemaj (aspoň ta implementace, co jsem používal). Ono, jakej by měl smysl?
Co se týče ==0 tak je to hezkej assemblerovskej hack, ale co jsem zkoušel, tak používá instrukci test, která narozdíl od or neukládá výsledek.
PS: Jinak za invektivu se poke, omlouvám :-) co se jí týče, máš recht :-)