Page 1 of 1

Hvilken type cast er bedst i dette tilfælde?

Posted: 16 Apr 2008, 16:14
by cluq
Dette indlæg er lidt en forlængelse af forrige indlæg omkring dårligt OO design, hvor dasapfe bla. nævner hvor ondt det gør i performance at have et dynamic_cast i sine inner loops.

Vedhæftet er et klasse diagram, hvor et objekt skal type castes fra imellem to klasser. Type casting er valid, dvs. objektet har den korrekte type, så castet vil ikke resultere i fejl eller et NULL object.

Det ville være oplagt at bruge dynamic_cast i det pågældende eksempel, men det er jo ret tungt, så hvad gør man så?

Man kan ikke bruge static_cast, da type casting'en ikke er i en direkte linje i inheritance træet. Reinterpret cast er også ubrugelig, da implementationen af denne eftersigende skulle være platform specifik, så den bør man nok ikke binde sig an på, medmindre man laver noget der kun skal compiles med én compiler, til ét OS.

Så er der kun almindelig C-like cast tilbage, dvs. Derived* pDerived = (Derived*)pBase;

Men hvor hurtig er den i forhold til dynamic_cast? Den laver selvfølgelig ikke de runtime checks som dynamic_cast laver.. Og er det grimt at bruge gammeldaws C-like type cast bare fordi man ikke kan bruge static_cast og fordi man ikke vil bruge dynamic_cast? Bliver man lynchet af OO-arkitekt nazierne? :)

(PS: Det lader til at det kun er mig der poster indlæg i kode-forummet, men hul i det - hyg hyg :))

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 16 Apr 2008, 18:53
by davidhelgason
Det virker nu især bare overdrevent kompliceret... hvilket problem prøver du at løse?

(ja ja, jeg er jo egentlig ikke koder mere... men jeg synes stadig det er nu altid sjovt at spekulere over .)

d.

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 16 Apr 2008, 20:41
by gjoel
Først: Enig med David!
cluq wrote:Det ville være oplagt at bruge dynamic_cast i det pågældende eksempel, men det er jo ret tungt, så hvad gør man så?
Jamen så gør da det! :) Problemstillingen hænger jo helt på hvor mange gange du skal udføre funktionen og hvor meget tid du har til det. Medmindre at dit klasse-hierarki er virkelig kompliceret (mere end selv det du har lavet), er dynamic_casts ikke et problem. Så lav den mest gennemskuelige og fejltolerante kode, og lad bekymringerne om performance vente til du kan se at der er et problem.
(man skal være varsom med at over-optimere sine programmer - det tager tid og kan, som her, give kode der har andre problemer).

- og den her flok posts om RTTI så ret fine ud :)
http://www.wilmott.com/messageview.cfm? ... adid=37956

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 16 Apr 2008, 22:12
by Gorm
Med fare for at starte en mindre flame war..

Efter min mening er det utroligt nemt at komme til overbruge nedarvning. Måske du kan løse dit problem nemmere, ved en kombination af nedarvning og aggregering istedet?

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 17 Apr 2008, 10:44
by cluq
Klassehierakiet er "set in stone", så det kan der ikke pilles ved. Men efter hvad jeg kan se ud fra linket postet af gjoel, så er der selvfølgelig mange meninger omkring RTTI, men at man nok ikke rigtig kan komme uden om det.

Jeg kan dog godt lide tanken om ikke at optimere, før man identificere et reelt performance problem. Jeg har dog nok mere en tendens til at optimere unødvendigt af ren nysgerrighed: "kan man lave den her stump kode hurtigere, og hvilke bottlenecks er der i den?" ;)

Men generelt gik mit spørgsmål lidt mere på hvordan et C-style cast løser problemet med at lave et cross-cast (som jeg så lige har lært at det hedder). Den må vel gøre det samme som et dynamic_cast, men uden diverse checks?

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 17 Apr 2008, 14:30
by gjoel
<edit>Nu kiggede jeg rent faktisk på dit diagram... :p så vidt jeg kan se bør du ikke kunne lave dit cast overhovedet. Det er fra en baseclass til en anden baseclass - de nedarver ikke fra en fælles klasse. Dynamic_cast compiler fordi den først laver typecheck runtime, og et c-style-cast laver nok en reinterpret_cast. Så vidt jeg lige kan se er designet fejlbehæftet... om end i sten.</edit>

Det er i øvrigt fint nok at optimere koden - man skal bare sørge for at det ikke bliver på bekostning af læselighed, anvendelighed, hvor let det er at debugge, eller stabilitet :) En anden ting er at det kun sjældent vil være signifikant at optimere på sprog-niveau som du lægger op til her. Som regel vil en bedre algoritme, f.eks. til sortering, give mere markante forbedringer.

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 20 Apr 2008, 16:46
by dasapfe
Du kan godt caste imellem de to klasser, men din compiler vil generere ugyldig kode, uanset hvilken slags cast det er. Et modificeret eksempel fra C++ FAQ Lite:

[kode]class Car : public Vehicle {
public:
virtual void startEngine();
virtual void openGasCap();
};

class NuclearSubmarine : public Vehicle {
public:
virtual void startEngine();
virtual void fireNuclearMissle();
};

int main()
{
Car car;
Car* carPtr = &car;
Car** carPtrPtr = &carPtr;
Vehicle** vehiclePtrPtr = reinterpret_cast<Vehicle**>(carPtrPtr);
NuclearSubmarine sub;
NuclearSubmarine* subPtr = &sub;
*vehiclePtrPtr = subPtr;
carPtr->openGasCap(); // This might call fireNuclearMissle()!
}[/kode]
Som du kan se, kan det gå grueligt galt :D

Man kan dog lave mange sjove ting med cast, såsom hurtig abs af floats, osv...

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 20 Apr 2008, 17:01
by jonaz.dk
@dasapfe
jeg disablede code tagget i din post.. det breaker desværre RSS feedet for nogle brugere. :(

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 20 Apr 2008, 17:09
by dasapfe
Hehe, helt iorden ;)

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 21 Apr 2008, 09:36
by cluq
Som du kan se, kan det gå grueligt galt
Ja, for din carPtr er jo blevet til en NuclearSubmarine*, og den har ingen openGasCap(). Jeg er ikke helt sikker på at jeg forstår din pointe? :) Men jeg har vedhæftet et simplifiseret klassediagram. Og min kode ser sådan ud:

A* aPtr = new C();
dynamic_cast<B*>(aPtr)->someMethod();

Men du har ret i at et C-like cast ville fejle - hvilket det også gjorde, da jeg prøvede at køre skidtet ;) Men et dynamic_cast magter opgaven.

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 22 Apr 2008, 14:40
by dasapfe
Ja, for din carPtr er jo blevet til en NuclearSubmarine*, og den har ingen openGasCap(). Jeg er ikke helt sikker på at jeg forstår din pointe?
Det var ikke helt det jeg ville frem til (selv om det er rigtigt nok). Det jeg prøvede at vise var, at misbrug af casting kan gå galt. I eksemplet var finten at begge klasser har 2 virtuelle funktion, og dermed og også et vtable med 2 elementer. Når du senere hen tvinger compileren til at behande en NuclearSubmarine som en Car, og kalder en af Car's virtuelle funktion, så henter den jo faktisk addressen på funktionen fra NuclearSubmarine's vtable, og dermed ender du med at kalde en af NuclearSubmarine's funktioner, selvom du skrev 'carPtr->openGasCap();'. Tredje verdenskrig startet fordi man ville have benzin på volvoen :D
Men jeg har vedhæftet et simplifiseret klassediagram. Og min kode ser sådan ud:
Haha, jeg har misforstået nedarvningen i dine diagrammer! Jeg troede det gik den anden vej, og derfor påstod jeg at det ikke kan lad sig gøre. Det kan det dog sagtens, og du behøves ikke engang at bruge dynamic_cast<>, et static_cast<> kan sagtens gøre det :)

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 22 Apr 2008, 19:31
by cluq
Det påstår Visual Studio at den ikke kan. Hvis jeg bytter mit dynamic_cast ud med et static_cast, får jeg følgende compile fejl:

error C2440: 'static_cast' : cannot convert from 'A *' to 'B *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast.

Hvis jeg laver et C-style cast, så breaker den runtime - castet resulterer i en bad pointer.

Det eneste der dur er dynamic_cast.

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 22 Apr 2008, 22:58
by dasapfe
Jeps du har ret, det var mig der så forkert, jeg havde set det som et cast fra A* til C* :(

Hvis du kan garantere at det altid kun er en A* der peger på en C* som skal castes til en B* ( :? ), så kan du undgå brugen af dynamic_cast<> som følger:

A* aPtr = new C();
static_cast<B*>((C*)aPtr)->someMethod();

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 23 Apr 2008, 00:16
by gjoel
- hvilket bringer os tilbage til hvad det c-style cast egentlig gør. Jeg føler mig ret overbevist om at det reinterpret_cast (uden at have kigget mere på det), så skriv da det i stedet (men lad være med at springe direkte til B*-castet, da du så får slicing).

Men brug da et dynamic_cast! (og check på om pointeren bliver 0 efter). Så opdager du også når du laver fejl (for det gør man :) ).

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 23 Apr 2008, 01:26
by dasapfe
Det skulle faktisk også have været et static_cast<>, men jeg var på vej ud af døren. Undgå som en regel c-style casts, de er mindre sikre. Det rettede kode:

A* aPtr = new C();
static_cast<B*>(static_cast<C*>(aPtr))->someMethod();

Men istedet for at hugge mere i det her, så bare behold dit dynamic_cast<>. Du kan altid optimere senere :)

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 25 Apr 2008, 23:31
by jalf
Der er ingen magi i et C-style cast. Det kan ikke noget du ikke ville kunne med de "rigtige" casts. Det prøver blot at anvende diverse C++-casts i prioriteret rækkefølge indtil det finder et der virker. (Tror nok at reinterpret_cast er det sidste den prøver, hvilket naturligvis altid vil virke så længe argumentet er en pointer eller reference)

Derfor er C-style casts en rigtig dum ide. Det har intet med OO at gøre. Det er et spørgsmål om om du har tænkt dig at bruge C eller C++. C++ casts kan alt hvad C cast kan, men gør det typesikkert.

Det største problem med C-style casts er simpelthen at du ikke umiddelbart aner hvilket cast den udfører. Den vælger et der ser ud til at compile.
Så vidt jeg husker prøver den følgende rækkefølge (Jeg kan tage fejl, check standarden hvis du vil være sikker)
const_cast
static_cast
reinterpret_cast

(Det er muligt at den forsøger et dynamic_cast et sted i rækkefølgen, men jeg tror det ikke)

Men det vil sige at der er absolut ingen undskyldning for at bruge et C cast. Det kan ikke noget de andre ikke kan, det gør det bare sværere for dig at forudsige hvordan din kode opfører sig.

Derudover findes der ikke et gyldigt cast i dit tilfælde. dynamic_cast ville være gyldigt hvis den ene klasse nedarvede (direkte eller indirekte) fra den anden, men det gør de ikke i dit tilfælde. De har i stedet en fælles baseclass. (reinterpret_cast ville mig bekendt stadig ikke være gyldigt, selvom det ville compile.)

(Strengt taget kunne du dynamic_caste til den fælles base, og derfra dynamic_caste til den anden klasse. Men det ville naturligvis altid give fejl på runtime, så du skubber bare problemet)

Re: Hvilken type cast er bedst i dette tilfælde?

Posted: 31 Dec 2008, 15:03
by ups101
Undskyld genoplivning.

Et enkelt static_cast er nok:

class A
{
public: virtual void SomeMethod() {OutputDebugString("FAIL A");}
};
class WhiteBox : public A
{
public: virtual void SomeMethod() {OutputDebugString("FAIL WhiteBox");}
};
class B
{
public: virtual void SomeMethod() {OutputDebugString("GOTCHA");}
};
class C : public WhiteBox, public B
{
public: virtual void SomeMethod() {OutputDebugString("FAIL C");}
};

void test()
{
A* pA = new C();
C* pC = static_cast<C*>(pA);
pC->B::SomeMethod();
}