Hvilken type cast er bedst i dette tilfælde?

cluq
Level 2 - Grain of sand
Posts: 24
Joined: 11 Mar 2008, 15:50

Hvilken type cast er bedst i dette tilfælde?

Unread post by cluq » 16 Apr 2008, 16:14

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 :))
Attachments
Class diagram
Class diagram
typecast.jpg (16.54 KiB) Viewed 27179 times

davidhelgason
Level 8 - Summoner
Posts: 86
Joined: 12 Mar 2008, 13:49

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

Unread post by davidhelgason » 16 Apr 2008, 18:53

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.

gjoel
Level 3 - Infant
Posts: 34
Joined: 17 Mar 2008, 13:15

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

Unread post by gjoel » 16 Apr 2008, 20:41

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

Gorm
Level 24 - Cyber demon
Posts: 243
Joined: 10 Mar 2008, 00:11
Location: London, UK
Contact:

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

Unread post by Gorm » 16 Apr 2008, 22:12

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?
Gorm - Senior Creative Technologist
Blog
Twitter
Global Game Jam

cluq
Level 2 - Grain of sand
Posts: 24
Joined: 11 Mar 2008, 15:50

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

Unread post by cluq » 17 Apr 2008, 10:44

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?

gjoel
Level 3 - Infant
Posts: 34
Joined: 17 Mar 2008, 13:15

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

Unread post by gjoel » 17 Apr 2008, 14:30

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

dasapfe
Level 1 - Speck of dust
Posts: 17
Joined: 08 Mar 2008, 16:10

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

Unread post by dasapfe » 20 Apr 2008, 16:46

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

User avatar
jonaz.dk
Site Admin
Posts: 4121
Joined: 04 Mar 2008, 22:24
Location: DK
Contact:

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

Unread post by jonaz.dk » 20 Apr 2008, 17:01

@dasapfe
jeg disablede code tagget i din post.. det breaker desværre RSS feedet for nogle brugere. :(

dasapfe
Level 1 - Speck of dust
Posts: 17
Joined: 08 Mar 2008, 16:10

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

Unread post by dasapfe » 20 Apr 2008, 17:09

Hehe, helt iorden ;)

cluq
Level 2 - Grain of sand
Posts: 24
Joined: 11 Mar 2008, 15:50

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

Unread post by cluq » 21 Apr 2008, 09:36

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.
Attachments
typecast2.jpg
typecast2.jpg (12.68 KiB) Viewed 26843 times

dasapfe
Level 1 - Speck of dust
Posts: 17
Joined: 08 Mar 2008, 16:10

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

Unread post by dasapfe » 22 Apr 2008, 14:40

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 :)

cluq
Level 2 - Grain of sand
Posts: 24
Joined: 11 Mar 2008, 15:50

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

Unread post by cluq » 22 Apr 2008, 19:31

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.

dasapfe
Level 1 - Speck of dust
Posts: 17
Joined: 08 Mar 2008, 16:10

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

Unread post by dasapfe » 22 Apr 2008, 22:58

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();

gjoel
Level 3 - Infant
Posts: 34
Joined: 17 Mar 2008, 13:15

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

Unread post by gjoel » 23 Apr 2008, 00:16

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

dasapfe
Level 1 - Speck of dust
Posts: 17
Joined: 08 Mar 2008, 16:10

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

Unread post by dasapfe » 23 Apr 2008, 01:26

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 :)

jalf
Level 0 - Null
Posts: 1
Joined: 25 Apr 2008, 23:17

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

Unread post by jalf » 25 Apr 2008, 23:31

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)

ups101
Level 0 - Null
Posts: 7
Joined: 31 Dec 2008, 14:40

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

Unread post by ups101 » 31 Dec 2008, 15:03

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

Post Reply