Člověče máš to nějaký složitý, a je v tom mnoho věcí naráz. Nicméně k čemu se umím vyjádřit je pomalost
dynamic_castu. Ano, ten je pomalý, ale ne proto, že by ti programátoři to neuměli napsat, ale ono to prostě líp nejde.
dynamic_cast musí na základě dosti omezených dat o objektu a požadované třídě najít adresu patřičného potomka a to s ohledem na různá vícenásobná a hlavně virtuální dědění (což je samozřejmě chuťovka). Navíc třeba ve Windows to řeší i přes různé knihovny, kdy každá knihovna může být přeložena jiným překladačem, takže neexistuje jedný způsob, jak identifikovat třídu, takže se používá zamanglované jméno a k porovnávání se používá funkce
strcmp.
Tohle jsem řešil taky s ruznými způsoby a stupni úspěchu a neúspěchu. A nakonec jsem skončil u řešení, které nazývám "
IInterface". Je to třída, která poskytuje funkci
getInterface (zkráceně
getIfc) a zapisuje se
Potomek &p = predek.getIfc<Potomek>()
nebo
Potomek *p = predek.getIfcPtr<Potomek>()
Ten vztah tak nemusí být jen potomek předek, ale lze takhle získat pointer na libovolné rozhraní, které objekt může přímo implementovat nebo i nepřímo. Třeba mám rozhranní
IHttpRequest, který také poskytuje
ITCPConnection a dokonce
IJobManager právě prostřednictvím
getIfcPtr. Přestože
IJobManager nemá nic společného s requestem, přesto to funguje jako service, kterou request nabízí svému handleru (handler si může spustit job)
Implementace tohoto prostředku mám ve své knihovně
lightspeedhttps://github.com/ondra-novak/lightspeed/blob/master/src/lightspeed/base/interface.hhttps://github.com/ondra-novak/lightspeed/blob/master/src/lightspeed/base/interface.tccSoubor *.tcc se inkluduje do každého *.cpp, kde se to používá, je to implementační část šablony.
Kupodivu to nefunguje automaticky tak jak by sis představoval. Když totiž nic neuděláš, rozhraní vše realizuje přes
dynamic_cast, takže žádná věda. Ale ušetří to spoustu práce, tam, kde rychlost nepotřebuješ řešit. Pokud chceš ale nabízet nějaké zkratky, nebo extrabuřty, stačí, když potomek implementuje funkci
virtual void *proxyInterface(IInterfaceRequest &p)
Funkce si z p vyzvedne typ třídy (jako
typeid) kterou chce volající získat a pokud ji umí, pak vrátí buď sebe přes
static_cast na požadovanou třídu nebo interface - musí ho pak přetypovat na void *, nebo přímo vrátí pointer na službu, která požadovanou třídu implementuje. Je akorát třeba pamatovat na to, že pointer musí být před vrácením nejprve přetypován na požadovaný typ přes
static_cast, než je převeden na void *, jelikož šablona si to na druhé straně zase přetypuje zpět na požadovanou třídu.
Funguje to v celku dobře, pokud mám někde místo, které dost často dělá přetypování a chci se vyhnout
dynamic_castu, tak v potomkovi definuju přímo onu zkratku a tím se vyhnu pomalému
dynamic_cast. Je to opravdu rychlejší, protože ve výsledku to je jen jedno volání virtuální funkce, jedna operace porovnávání a
static_cast nic nestojí.
Samozřejmě, jakmile se mi těch tříd co nabízím začne množit víc, začne být ten rozhodovací mechanismus složitejší a pak je třeba si spočítat, jestli náhodou ten
dynamic_cast nevyjde rychlejší. Potom stačí pouze zavolat výchozí implementaci
IInterface a on už to zařídí sám.