[C++] non-virtual/final?

Pagina: 1
Acties:

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Stel je hebt een base class Ca met een pure virtual method f.
Je hebt een daarvan afgeleide class Cb met een virtual method f.
Je hebt Cb* b en b->f();
Is het dan mogelijk om ervoor te zorgen dat die aanroep van f geen gebruik maakt van de virtual function table maar direct Cb::f aanroept?

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Als de compiler zich ervan kan vergewissen dat b inderdaad naar een Cb-object wijst (omdat je die net hebt initialiseert, bijvoorbeeld) kan de compiler als optimalisatie een statische call maken. Dit is echter slechts een optimalisatie (die niet per se uitgevoerd hoeft te worden door een specifieke compiler).

Je kunt ook handmatig een statische call doen; dan moet je gewoon de base class waarvan je de methode wilt hebben erbij vermelden:
C++:
1
    b.Cb::f();

(Cb is een klasse, b is een instantie van een (sub)klasse van Cb, f is een methode die in Cb gedefinieerd is).

Ik zou dit echter alleen gebruiken als je functionaliteit van een specifieke subklasse nodig hebt en niet bij wijze van optimalisatie. Als b namelijk niet van klasse Cb is maar van een afgeleide klasse daarvan, dan is de werking niet meer equivalent aan die van een dynamische (virtual) call. Dat is ook juist het hele punt van een statische call, natuurlijk.

[ Voor 36% gewijzigd door Soultaker op 16-04-2003 12:45 ]


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Dat snap ik, maar ik vroeg me af of dit ook op te lossen was door alleen de method declaratie aan te passen zonder alle calls zelf aan te passen.

[ Voor 79% gewijzigd door Olaf van der Spek op 16-04-2003 12:51 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:46
Als je dat wilt, waarom maak je dan een virtuele functie?

https://fgheysels.github.io/


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 13-08 16:46

curry684

left part of the evil twins

whoami schreef op 16 April 2003 @ 15:28:
Als je dat wilt, waarom maak je dan een virtuele functie?
Omdat je in je base class functionaliteit wil introduceren zonder te implementeren?

En omdat latere OOP-talen zoals java wel een 'final' keyword hebben. Ik heb vandaag toevallig weer de discussie gehad over de pro's en con's van 'final', en ik persoonlijk blijf erbij dat 'final' the enemy is wat betreft OOP.

Professionele website nodig?


  • GarBaGe
  • Registratie: December 1999
  • Laatst online: 15:48
curry684 schreef op 16 April 2003 @ 15:51:
[...]

Omdat je in je base class functionaliteit wil introduceren zonder te implementeren?

En omdat latere OOP-talen zoals java wel een 'final' keyword hebben. Ik heb vandaag toevallig weer de discussie gehad over de pro's en con's van 'final', en ik persoonlijk blijf erbij dat 'final' the enemy is wat betreft OOP.
In Java is "final" de enige (d8 ik) manier om een constante te definieren (wat betreft variabele)

Ryzen9 5900X; 16GB DDR4-3200 ; RTX-4080S ; 7TB SSD


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

GarBaGe schreef op 16 April 2003 @ 15:56:
[...]


In Java is "final" de enige (d8 ik) manier om een constante te definieren (wat betreft variabele)
en dat slaat ook helemaal nergens op, maar java kent ook geen const (dus dan maar een keyword gebruiken wat er op lijkt, toch? ;))
En als we toch over java bezig zijn: ik heb vanmiddag weer enorm zitten :r'en op het feit dat java geen default arguments kent (da's leuk, als je een C++ lib moet porten naar java, en je dan een functie hebt met 15 argumenten, waarvan er 12 default zijn)

Maar ontopic: niet zozeer final (want dan kun je er als derived class niets meer mee), maar meer gewoon een soort van nonvirtual, waarmee je een virtual methode weer niet virtual kan maken

[ Voor 40% gewijzigd door .oisyn op 16-04-2003 16:25 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:46
curry684 schreef op 16 April 2003 @ 15:51:
[...]

Omdat je in je base class functionaliteit wil introduceren zonder te implementeren?
Waarom maak je dan geen gebruik van interfaces?

https://fgheysels.github.io/


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

whoami schreef op 16 April 2003 @ 17:30:
[...]

Waarom maak je dan geen gebruik van interfaces?
1) C++ kent geen interfaces
2) Hoe wil je dat gebruiken als oplossing voor het oorspronkelijke probleem?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:46
.oisyn schreef op 16 april 2003 @ 17:33:
[...]


1) C++ kent geen interfaces
8)7
De warmte, en het gepraat over Java heeft mij verward.
2) Hoe wil je dat gebruiken als oplossing voor het oorspronkelijke probleem?
De TS wil een functie introduceren in z'n base class, maar die mag daar nog niet geimplementeerd zijn (pure virtual dus). Waarom gebruikt ie dan uberhaupt class-inheritance als hij die method ook gewoon dmv een interface kan implementeren.
Maarja, interfaces bestaan niet in C++.

Waarom dan geen abstracte class met een functie die geen body heeft (niet geimplementeerd is dan) ? Of bestaat dat ook niet in C++, abstract classes?

https://fgheysels.github.io/


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

whoami schreef op 16 April 2003 @ 17:35:
De TS wil een functie introduceren in z'n base class, maar die mag daar nog niet geimplementeerd zijn (pure virtual dus). Waarom gebruikt ie dan uberhaupt class-inheritance als hij die method ook gewoon dmv een interface kan implementeren.
Maarja, interfaces bestaan niet in C++.
Ik snap je niet
Je kunt toch gewoon een abstracte class hebben met een functie die nog niet geimplementeerd is (een abstracte functie, dus die class is ook gelijk abstract)?

Zo is het bij OlafvdSpek nu ook
Het probleem is alleen dat hij niet wilt dat de virtual table wordt geraadpleegd als het een pointer naar de derived class betreft. Dit zou alleen maar kunnen als de compiler weet heeft van het feit dat er geen derived class van die derived class is die die functie override.

En dat kun je de compiler dan evt. laten weten door 'm als final te definieren (maar dan kun je m niet meer overriden in een derived class), of iets als een nonvirtual keyword als ik voorstelde, zodat de compiler ook weet dat er geen verdere herdefinitie van die functie kan zijn
Of bestaat dat ook niet in C++, abstract classes?
Een class met een of meer pure virtual methodes is altijd abstract

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:46
.oisyn schreef op 16 April 2003 @ 17:41:
[...]


Ik snap je niet
Je kunt toch gewoon een abstracte class hebben met een functie die nog niet geimplementeerd is (een abstracte functie, dus die class is ook gelijk abstract)?

Zo is het bij OlafvdSpek nu ook
Het probleem is alleen dat hij niet wilt dat de virtual table wordt geraadpleegd als het een pointer naar de derived class betreft. Dit zou alleen maar kunnen als de compiler weet heeft van het feit dat er geen derived class van die derived class is die die functie override.

En dat kun je de compiler dan evt. laten weten door 'm als final te definieren (maar dan kun je m niet meer overriden in een derived class), of iets als een nonvirtual keyword als ik voorstelde, zodat de compiler ook weet dat er geen verdere herdefinitie van die functie kan zijn


[...]


Een class met een of meer pure virtual methodes is altijd abstract
Ik heb het op een niet virtuele, niet geimplementeerde functie. Dan moet die virtual table toch niet geraadpleegd worden?

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{
   public:
        void AFunction() {}

};

class B : public class A
{

   public:
       void  AFunction()
       {
             blaat.
       }
};

https://fgheysels.github.io/


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

Maar nu gaat het fout als je een A * hebt, dan wordt B::AFunction niet aangeroepen als het een B betreft

OlafvdSpek wil dit:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct A
{
    virtual void func () = 0;
};

struct B : public A
{
    void func () { }
};

void funcA (A * a)
{
    a->func ();  // nu moet de virtual table geraadpleegd worden
}

void funcB (B * b)
{
    b->func (); // nu niet, ook al is er een C die derived van B
}

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Ik zou het maar naar vinden als dat mogelijk zou zijn.
Zoals Curry al opmerkt draai je de herbruikbaarheid van die classe per direct de nek om. Het is niet meer mogelijk voor subclasses om het gedrag te specificeren welke voor hen nodig is. Daarmee kun je dus alleen nog maar subclasses maken welke niet totaal flexibel zijn.

Echter in de geest van C++ kan ik het nog wel begrijpen, maar als ik weer eens een final method in java zie, dan vraag ik me altijd af of dat nou wel zo nodig is.

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
.oisyn snapt het.
De reden voor nonvirtual is inderdaad alleen performance. Dat Cb daarna niet meer goed als parent class kan dienen snap ik.
Dat ik nog wel een virtual functie wil gebruiken is omdat ik soms wel via de virtual function table een call wil afhandelen.

[ Voor 11% gewijzigd door Olaf van der Spek op 17-04-2003 13:04 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 21:46
Maakt die performance zoveel uit dan?
Ik kan me voorstellen dat die maar milliseconden niet zoveel verschil zullen maken?

https://fgheysels.github.io/


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Paar ms?
Het is toevallig een functie die een paar duizend keer per sec wordt aangeroepen, dus die 'paar ms' zou zeer zeker uit maken.
Maar het kost echt geen ms. Waarschijnlijk kost het nog geen 10 cycles extra.

[ Voor 41% gewijzigd door Olaf van der Spek op 17-04-2003 13:08 ]


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Het kost je een lookup in de virtual table, maar ook nog een pointer extra + een vtable die per class opgeslagen moet worden :)

  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Een vtable is toch nodig, voor calls via de baseclass. En zo'n lookup kost geen ms.

[ Voor 95% gewijzigd door Olaf van der Spek op 17-04-2003 13:45 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

Glimi schreef op 17 april 2003 @ 13:36:
Het kost je een lookup in de virtual table, maar ook nog een pointer extra + een vtable die per class opgeslagen moet worden :)
vergeet niet dat de al gefetchte instructies uit de pipeline gepleurd kunnen worden omdat een call naar een pointer niet te voorspellen is... Functie pointers zijn niet goed voor de branch prediction :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Welke instructies worden er dan gefetched?
En volgens mij zijn functie pointers helemaal niet zo slecht voor de branch prediction, de branch conditie is namelijk constant en dus al vroeg genoeg bekend om de juiste instructies te fetchen.

[ Voor 9% gewijzigd door Olaf van der Spek op 17-04-2003 13:54 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

De pipelines van hedendaagse processoren zijn vrij lang, zo'n 10 tot 20 instructies.
Vergeet niet dat die functie een pointer naar een pointer naar een pointer is. Je hebt allereerst de pointer naar de instantie van de class, dat is 1. Die heeft een verwijzing naar de vtable, dat is 2. In de vtable staat op een bepaalde index de pointer naar de functie, 3.

Naar asm vertaald wordt dat zoiets:
GAS:
1
2
3
mov eax, [ebp-0x8]   ; de parameter (Cb *)
mov eax, [eax + 0]    ; vtable
call [eax + 8]    ; functiepointer


dit is dus onmogelijk te voorspellen, aangezien die call volledig afhankelijk is van de vorige instructie. En dus moet de hele pipeline weer leeggehaald worden

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Dat is toch een pipeline stall en geen pipeline flush?
Als er inderdaad niks voor de call gebeurd zit je met een stall. Een ander performance issue is dat virtual functies niet geinlined kunnen worden.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

Het is geen stall, je moet bedenken dat alle instructies die na de call volgen ook al zijn gefetched. Maar die call springt naar een onverwacht adres, en dus moeten alle instructies op dat nieuwe adres eerst weer gefetched worden. De hele instructiebuffer moet dus opnieuw gevuld worden

Maar dit is trouwens theoretisch geblaat, ik heb het een keer in de praktijk getest, en toen maakte het vrijwel niets uit ;)

[ Voor 18% gewijzigd door .oisyn op 17-04-2003 14:42 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
Waarom zou de CPU instructies na de call fetchen nadat bekend is dat het om een call gaat?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je denkt aan de verkeerde kant. Dat hoeft idd ook niet, maar dat maakt ook niet uit. Het gaat erom dat nadat de call gedaan is, alle instructies opnieuw gefetched en gedecodeerd en nog dat hele proces moeten ondergaan alvorens ze worden uitgevoerd. Daar zit 't m nou juist in

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
Afgezien van het correcte punt van .oisyn over de benodigde fetch is er nog het parktische probleem dat er (in theorie) na de call geen code meer hoeft te zijn, dat je dus een AV zou krijgen als je leest. Nou verwacht ik dat Borland weet of dat kwaad kan, en zo ja daar een NOP plempt, maar evengoed is het onnodige read die caches vervuilt e.d.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
MSalters schreef op 17 April 2003 @ 21:09:
Afgezien van het correcte punt van .oisyn over de benodigde fetch is er nog het parktische probleem dat er (in theorie) na de call geen code meer hoeft te zijn, dat je dus een AV zou krijgen als je leest. Nou verwacht ik dat Borland weet of dat kwaad kan, en zo ja daar een NOP plempt, maar evengoed is het onnodige read die caches vervuilt e.d.
Is er een requirement dat er na een call geldige code moet staan dan?
Zo niet, lijkt dit me een probleem van de CPU designer en niet de compiler schrijver.
Pagina: 1