Pro C++ experty.

Re:Pro C++ experty.
« Odpověď #30 kdy: 04. 12. 2015, 09:00:59 »
Tohle je úplně uhozený způsob udržení binární kompatibility, nebude to fungovat. Všichni se to snaží tazateli říct, ale on to nechce pochopit. Pak přijde "odborník" a doporučí podobnou blbost.

Nebude to fungovat třeba z toho důvodu, že default constructor je inline, takže se nevykoná v dynamické libce, ale u klienta. Správná velikost na správnou inicializaci objektu nestačí, musí být splněna celá řada další podmínek.

Já jsem taky neříkal, že odpovím tazateli, diskuze se zvrhla mezi účastníky, kteří netuší, k čemu se přetížený new používá.

No budiž.

Tedy pokud jde o binární kompatibilitu, tam se to takhle vůbec nemůže dělat. Ať zapomene na new, ať vůbec zapomene na to, že by do hlavičkových souborů dával finální třídy. Z knihovny může exportovat jen rozhraní, tedy třídy obsahující __pouze__ abstraktní funkce. A i tady bude mít problém, pokud bude do takového rozhraní přidávat funkce, musí nutně ta rozhraní verzovat.

Úplně první co z knihovny vypadne je nějaké základní rozhraní, které zpravidla obsahuje továrnu pro všechny objekty, které knihovna umí vytvořit.

Kód: [Vybrat]
class IMain {
public:
   virtual IFoo *createFoo() = 0;
   virtual IBar *createBar(...) = 0;
   virtual ~IMain() {}
}

Pokud je třeba, aby knihovna volala nějaké věci z hlavního programu, je jí to třeba dodat také přes rozhraní. Komunikace mezi knihovnou a hlavním program nelze zajistit lépe, než právě takhle. To je jediný čistý způsob jak to udělat.

Navázání komunikace s knihovnou se zpravidla děje přes C-čkovskou funkci (extern "C"). Tohle dobře funguje ve Windows.

Ve Windows je ještě jedna potíž a to s oddělenými heapy knihovny a hlavního programu. Proto není možné objekt vytvořený knihovnou zničit v hlavním programu a naopak. Rozhodně nezapomínat na virtuální destruktor, který tohle řeší.

(Pokud se ve windows použijí DLL runtime knihovny, pak je to trochu lepší, protože všechny alokace patří knihovně MSVCRT.DLL - stačí ale aby jeden modul měl statické linkování a pak jeho alokace patří pouze jemu. - V linuxu je jeden heap, tam to problém není)

Takže shrnutí. Binární kompatibilita jen přes interfacy (a to ještě verzované). Jinak lze využít dynamické knihovny i bez této nutnosti, za předpokladu, že s každou změnou hlavičkových souborů vydám novou verzi dynamické knihovny a budu je verzovat. Ale to není binární kompatibilita. Navíc člověka umlátí zejména v linuxu dependency-hell (ve Windows to řeší DLL-hell a winsxs-hell)


PS: teď mě napadlo, že pokud používá výjimky, taky si užije legrace, až bude výjimku z knihovny chytat v hlavním kódu. Třeba ve windows, když propadá výjimka z hlavního programu do knihovny přes callback a zpět do hlavního programu, stávalo se, že se neodchytla a spadla až do unexpected()


Ivan

Re:Pro C++ experty.
« Odpověď #31 kdy: 04. 12. 2015, 09:50:56 »
Hlavně i kdyby nějak vyřešil ten problém s new, tak pořád tu bude možnost, že uživatel knihovny bude chtít vytvořit instanci té třídy na zásobníku.

Divim se, za tady jeste nepadlo slovo "tovarna". Ja napriklad pouzivam tohle:
https://github.com/tora-tool/tora/blob/master/extlibs/loki-extra/include/loki/Factory_alt.h

Pouziva se to napr. takhle

std::auto_ptr <SQLParser::Statement> stat = StatementFactTwoParmSing::Instance().create("OracleDML", m_lastText, "");

SQLParser::Statement je zakladni trida, jejiz header includuju. Zadny dalsi headery nepotrebuju. Headery implementaci me nezajimaji.
Tovarna mi vytvori pointer na tridu, ktera se zaregistrovala se jmenem "OracleDML".

Tovarna je deklarovana jako:

typedef TORA_EXPORT Util::GenericFactory<SQLParser::Statement, LOKI_TYPELIST_2(const QString &, const QString&)> StatementFactTwoParm;
class TORA_EXPORT StatementFactTwoParmSing: public ::Loki::SingletonHolder<StatementFactTwoParm> {};


V C++ existuji i jine zpusoby ja zakazat alokaci na zasobniku.

k

Re:Pro C++ experty.
« Odpověď #32 kdy: 04. 12. 2015, 10:23:03 »
budu mit třídu exportovanou dynamickou knihovnou, např. class TridaZKnihovny.

Pokud v programu který tuto knihovnu používá vytvořím proměnnou
TridaZKnihovny* instance = new TridaZKnihovny();
tak mi kompilátor vygeneruje kód závislý na velikosti, kterou odvodí třeba z hlavičkového souboru.
Takže když bych updatoval tu dyn. knihovnu, tak mi to může rozbít ten program, protože se velikost toho
typu může změnit.

Teď ta otázka: Potřeboval bych teda zaručit, že new alokouje správnou velikost. V C++ můžete přetížit operátor new.

Děláš to blbě. Správné řešení je zavolat ne-oop funkci v knihovně, ta vytvoří instanci podle toho jak potřebuje a z knihovny si následně vyžádat rozhraní alias tabulka pointerů na metody. Každá nová funkcionalita má svoji novou tabulku.
Jedna z možných implementací je technologie COM a QueryInterface.

bjarne

Re:Pro C++ experty.
« Odpověď #33 kdy: 04. 12. 2015, 15:12:26 »
Nebude to fungovat třeba z toho důvodu, že default constructor je inline, takže se nevykoná v dynamické libce, ale u klienta. Správná velikost na správnou inicializaci objektu nestačí, musí být splněna celá řada další podmínek.

Hlavně i kdyby nějak vyřešil ten problém s new, tak pořád tu bude možnost, že uživatel knihovny bude chtít vytvořit instanci té třídy na zásobníku.

To jsem mel poresene. V debug nastaveni (tzn. pri vyvoji) je new operator a konstruktory privatni, takze na stacku nikdo nic nevytvori, stejne tak nepouzije normalni new, ale musi pres makro, ktere se nahrazuje volanim metody ve tride, ktera je jako friend. No pridam cely ten kod. Na C++ nejsem odbornik, takze jsem nejake veci nemel domyslene. Hlavne to, ze jsem myslel, ze volani virtualni metody tridy z knihovny jde pres tabulku importu, jenze on ten kompilator generuje kod i tak pres pointer na tabulku virtualnich metod, takze to co jsem chtel celkove udelat stejne nebude fungovat.

Kód: [Vybrat]
#ifdef QT_DEBUG
#define DEBUG
#endif

#ifdef DEBUG
#define CUSTOM_NEW
#define core private
#else
#define core public
#endif

#ifdef CUSTOM_NEW
#define NEW(TYPE) Memory::nnew<TYPE>
#define DEL(obj) Memory::ndel(obj)
#else
#define NEW(Type) new /*(std::malloc(Type::getSize()))*/ Type
#define DEL(obj) delete obj
#endif

#define SIZE_HANDLER_NAME _n_core_get_size

#define CORE_START(Class) \
    SIZE_HANDLER(Class) \
    core: \
    MEM_HANDLERS(Class) \

#define CORE_END \
    private:

#define SIZE_HANDLER(Class) \
    static size_t SIZE_HANDLER_NAME() { return sizeof(Class); }

#define MEM_HANDLERS(Class) \
    void* operator new (size_t size) { /*std::cout << "new called" << std::endl;*/ return std::malloc(Class::SIZE_HANDLER_NAME()); } \
    void* operator new (size_t size, void* placement) { /*std::cout << "new placement called" << std::endl;*/ return placement; } \
    void operator delete (void* mem) { std::free(mem); } \
    void operator delete[] (void* mem) { std::free(mem); } \
    friend class Memory;

class Base {

    CORE_START(Base)

    Base(int i, int j, int k, int l) { m_i = i; m_j = j; m_j = j; m_k = k; }

    CORE_END

public:
    int m_i;
    int m_j;
    int m_k;
    int m_l;

};

class Memory {

public:
template <typename T, typename... Args>
static /* typename std::enable_if<std::is_base_of<HeapObject, T>::value, T*>::type*/ T* nnew(Args... args) {

    return new T(args...);

}

static void ndel(void* obj) {

    delete obj;
}


};

#define ITER 10000000

int main(int argc, char* argv[]) {

    QElapsedTimer timer;
    timer.start();

    for (int i = 0; i < ITER; i++) {
        auto b = NEW(Base)(10, 20, 50, 70);
        DEL(b);
    }



    std::cout << "TIME: " << timer.elapsed() << std::endl;

    return 0;

}

bjarne

Re:Pro C++ experty.
« Odpověď #34 kdy: 04. 12. 2015, 15:16:23 »
Jinak přetěžování new v C++ běžně používám. Zpravidla ale jako nějakou base třídu, z níchž pak dědím, abych ten způsob alokace dostal všude tam, kde ho potřebuju. Třeba tady

Tohle je úplně uhozený způsob udržení binární kompatibility, nebude to fungovat. Všichni se to snaží tazateli říct, ale on to nechce pochopit. Pak přijde "odborník" a doporučí podobnou blbost.

Nebude to fungovat třeba z toho důvodu, že default constructor je inline, takže se nevykoná v dynamické libce, ale u klienta. Správná velikost na správnou inicializaci objektu nestačí, musí být splněna celá řada další podmínek.

Ale ja to chci pochopit, naopak. Akorat jsem nerekl vsechny detaily, treba ze se obejdu bez toho, aby ty tridy z jadra sly v pluginech subclassovat apod.. Proste mel jsem to vymyslene tak, ze jsem byl opravdu presvedceny, ze by to fungovalo a mohl bych se vyvarovat tomu vzoru opaque pointer/pimpl (pokud budu v pohode, kdyz nektere veci nepujdou).



bjarne

Re:Pro C++ experty.
« Odpověď #35 kdy: 04. 12. 2015, 15:59:58 »
Vyjimky pouzivat nehodlam, to uz jsem zachytil, ze v C++ je to resene nejak blbe.

Jinak ja tady popisu, pokusim se co nejsrozumetelneji (vyjadrovani zrovna neni moje silna stranka v posledni dobe), ceho chci vlastne dosahnout.

Takze:

Bude aplikace a vystupem prekladu bude krome te vlastni aplikace taka rada dynamickych knihoven, se kterymi se budou linkovat zasuvne moduly (take ve forme dynamickych knihoven) do aplikace. Proste klasika, kdyz chcete mit aplikaci, do ktere je mozne vytvaret nejake pluginy.

Jak ale zajistim, abych mel co nejprijemnejsi (hlavne flexibilita zmen, coz je problem, kdyz nesmis rozbit tu kompatibilitu) zpusob vyvoje toho jadra?

Chtel bych mit moznost pouzivat java-like interface. Vim, ze v C++ takova moznost neni a muzu vytvorit jen C++ tridu s ciste virtualnima metodama.

V aplikaci bude spousta modelu, kdy je vzdy jedna bazova trida, ze ktere ostatni dedi a navic jeste pridavaji radu interfacu. Typickym prikladem AST strom, kdy je nejaka bazova trida Node a pak subclassy jako ExpressionNode, VariableNode,... a tyhle tridy navic jeste krome tridy Node dedi ruzne interfacy jako IContext atd.. Tech interfacu trida muze dedit treba 10.

Chci mit neco jako operator instanceof v Jave. C++ ma dynamic cast a Qt qobject_cast, ale to mi nevyhovuje, protoze dynamic cast je pomalej a qt zase neumoznuje castnout interface zpatky na tu implementujici tridu.

Mel bych moct delat veci jako

Node* node = ...
if (IContext* ctx = my_cast(node, IContext) { ... }

ALE chci taky moct castit z rozhrani zpet (to uz je v Qt problem, nejde to a dynamic_cast je pomalej a celkove nevim jeho dalsi nevyhody a jestli to vubec jde)

if (VariableNode* var = my_cast(ctx, VariableNode) { .... }

To casteni uz mam vyresene, alespon dokud se neobjevi dalsi problemy.

Ted jde o to, jak teda pojmout ten vyvoj trid jadra, abych tam mohl provadet zmeny jako pridavani i virtualnich metod do libovolne tridy v hierarchii dedeni, pridavani dalsich trid do hierarchie dedeni apod.. Co uz jsem pochopil, hlavne z toho odkazu co tu daval Gamer na to KDE (diky, super odkaz), tak tohle je proste problem a nejde to. TAKZE CO TED, mam se na to vykaslat? To asi nepujde, musim to nejak vymyslet, ale zase aby to nebylo brutalne neefektivni, protoze to uz bych mohl to delat v Jave (ba ne nemuzu, potrebuju pouzivat Qt).

Proc jsem se tady na zacatku ptal na ten operator new bylo protoze jsem se domnival, ze ten problem je hlavne v tom, ze kompilator nesmi v pluginu generovat kod, co je zavisli na velikosti tech trid z jadra, takze jsem se snazil vymyslet, jak tu velikost zjistit az za behu a podle toho alokovat dostatecny prostor. BOHUZEL mi pak doslo, ze problem je i v tom pridavani virtualnich metod, pridavani trid do hierarchie apod.. Tohle jsem myslel, ze bude ok, protoze jsem pocital s tim, ze zadny datovy cleny nebudu pouzivat primo, ale pres metody a naivne jsem se domnival, ze volani metod jde prece pres tabulku importu (o pointerech do TVM jsem vedel, ale myslel jsem i tak, ze ten kod pouzivajici ty pointery proste bude v te knihovne a ne v pluginu).

Jinak vazim si vasich postrehu, diky za ne.

bjarne

Re:Pro C++ experty.
« Odpověď #36 kdy: 04. 12. 2015, 16:20:50 »

Úplně první co z knihovny vypadne je nějaké základní rozhraní, které zpravidla obsahuje továrnu pro všechny objekty, které knihovna umí vytvořit.

Kód: [Vybrat]
class IMain {
public:
   virtual IFoo *createFoo() = 0;
   virtual IBar *createBar(...) = 0;
   virtual ~IMain() {}
}


A kdyz ted  v moji aplikaci bude IFoo interface k nejake implementaci FooImpl, ktera bude dedit ze tridy BarImpl, k niz bude to rozhrani IBar, tak by to rozhrani IFoo melo dedit z rozhrani IBar, jenze to uz jsem zase v situaci, kdy pak nemuzu do rozhrani IBar pridat nejakou virtualni metodu. aniz bych nerozbil kod co pouziva IFoo (co jsem pochopil, tak leaf classam muzu virtualni metody pridavat na konec - krome windows, ten to pry nejak muze preorganizovat). Resenim by bylo zkopirovat ty metody z rozhrani IBar do IFoo, ale jako tohle fakt delat nechci. Chapu to spravne?

Re:Pro C++ experty.
« Odpověď #37 kdy: 04. 12. 2015, 16:28:49 »
Č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


Kód: [Vybrat]
Potomek &p = predek.getIfc<Potomek>()

nebo

Kód: [Vybrat]
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ě lightspeed

https://github.com/ondra-novak/lightspeed/blob/master/src/lightspeed/base/interface.h
https://github.com/ondra-novak/lightspeed/blob/master/src/lightspeed/base/interface.tcc

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

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

Sten

Re:Pro C++ experty.
« Odpověď #38 kdy: 04. 12. 2015, 16:30:20 »
Vyjimky pouzivat nehodlam, to uz jsem zachytil, ze v C++ je to resene nejak blbe.

Jak blbě?

Chtel bych mit moznost pouzivat java-like interface. Vim, ze v C++ takova moznost neni a muzu vytvorit jen C++ tridu s ciste virtualnima metodama.

Třída s čistě virtuálními metodami je v C++ to samé, jak v Javě interface. C++ má vícenásobnou dědičnost, nepotřebuje speciální interfacy.

Chci mit neco jako operator instanceof v Jave. C++ ma dynamic cast a Qt qobject_cast, ale to mi nevyhovuje, protoze dynamic cast je pomalej a qt zase neumoznuje castnout interface zpatky na tu implementujici tridu.

To říká kdo, že je dynamic_cast pomalejší oproti instanceof? V GCC a Clangu je implementovaný prakticky totožně jako v JVM.

dynamic_cast je pomalý, ale to je i instanceof.

dynamic_cast je pomalej a celkove nevim jeho dalsi nevyhody a jestli to vubec jde

Jde to, to je právě jeho účel. Pomalý je úplně stejně jako přetypovávání v Javě.

Ted jde o to, jak teda pojmout ten vyvoj trid jadra, abych tam mohl provadet zmeny jako pridavani i virtualnich metod do libovolne tridy v hierarchii dedeni, pridavani dalsich trid do hierarchie dedeni apod.. Co uz jsem pochopil, hlavne z toho odkazu co tu daval Gamer na to KDE (diky, super odkaz), tak tohle je proste problem a nejde to. TAKZE CO TED, mam se na to vykaslat? To asi nepujde, musim to nejak vymyslet, ale zase aby to nebylo brutalne neefektivni, protoze to uz bych mohl to delat v Jave (ba ne nemuzu, potrebuju pouzivat Qt).

C++ má na tohle trik: virtuální dědičnost. Pokud nezměníte pořadí již definovaných metod v rozhraních, pak klidně můžete přidávat další. (Alespoň v implementaci GCC + Clang.)

Re:Pro C++ experty.
« Odpověď #39 kdy: 04. 12. 2015, 16:33:50 »

A kdyz ted  v moji aplikaci bude IFoo interface k nejake implementaci FooImpl, ktera bude dedit ze tridy BarImpl, k niz bude to rozhrani IBar, tak by to rozhrani IFoo melo dedit z rozhrani IBar, jenze to uz jsem zase v situaci, kdy pak nemuzu do rozhrani IBar pridat nejakou virtualni metodu. aniz bych nerozbil kod co pouziva IFoo (co jsem pochopil, tak leaf classam muzu virtualni metody pridavat na konec - krome windows, ten to pry nejak muze preorganizovat). Resenim by bylo zkopirovat ty metody z rozhrani IBar do IFoo, ale jako tohle fakt delat nechci. Chapu to spravne?

Můžeš použít moje řešení IInterface (viz výše) ale samozřejmě musíš na to myslet úplně na začátku, kdy každé rozhraní dědí IInterface. To pak každé rozhraní poskytuje funkci getIfc<typ> a každý ten BarImpl nebo FooImpl může na základě požadavku vrátit pointer na rozhraní, které požaduješ. Je to hodně podobné tomu, co dělá ve windows QueryInterface. Akorát QueryInterface vyžaduje transitivitu a reflexivitu, což moje řešení ne. QueryInterface je blíže spíš dynamic_castu, zatímco mé řešení spíš připomíná funkci QueryService, které zahrnuji i QueryInterface.

Pak můžeš napsat

Kód: [Vybrat]
IFoo *foo = main->createFoo();
IBar *p = foo->getIfc<IBar>()

Je v2c9 FooImpl, aby si zařídilo převod z IFoo na IBar. Třeba tak, že bude třída FooBar, která dědí BarImpl a FooImpl

Re:Pro C++ experty.
« Odpověď #40 kdy: 04. 12. 2015, 16:45:54 »
To jsem mel poresene. V debug nastaveni (tzn. pri vyvoji) je new operator a konstruktory privatni, takze na stacku nikdo nic nevytvori, stejne tak nepouzije normalni new, ale musi pres makro, ktere se nahrazuje volanim metody ve tride, ktera je jako friend.

Hmm, ta knihovna teda ty konstruktory sice bude exportovat, ale přesto by neměly být používány. Takže uživatel dostane speciální makro, které bude při vývoji hlídat, zda to náhodou neudělal. No, pominu-li, že mi to přijde ulítlé, tak jen dodám, aby sis dal pozor na implicitní konstruktory, protože jejich volání jsi nezabránil.

bjarne

Re:Pro C++ experty.
« Odpověď #41 kdy: 04. 12. 2015, 17:14:52 »
Vyjimky pouzivat nehodlam, to uz jsem zachytil, ze v C++ je to resene nejak blbe.

Jak blbě?

Chtel bych mit moznost pouzivat java-like interface. Vim, ze v C++ takova moznost neni a muzu vytvorit jen C++ tridu s ciste virtualnima metodama.

Třída s čistě virtuálními metodami je v C++ to samé, jak v Javě interface. C++ má vícenásobnou dědičnost, nepotřebuje speciální interfacy.

Chci mit neco jako operator instanceof v Jave. C++ ma dynamic cast a Qt qobject_cast, ale to mi nevyhovuje, protoze dynamic cast je pomalej a qt zase neumoznuje castnout interface zpatky na tu implementujici tridu.

To říká kdo, že je dynamic_cast pomalejší oproti instanceof? V GCC a Clangu je implementovaný prakticky totožně jako v JVM.

dynamic_cast je pomalý, ale to je i instanceof.

dynamic_cast je pomalej a celkove nevim jeho dalsi nevyhody a jestli to vubec jde

Jde to, to je právě jeho účel. Pomalý je úplně stejně jako přetypovávání v Javě.

Ted jde o to, jak teda pojmout ten vyvoj trid jadra, abych tam mohl provadet zmeny jako pridavani i virtualnich metod do libovolne tridy v hierarchii dedeni, pridavani dalsich trid do hierarchie dedeni apod.. Co uz jsem pochopil, hlavne z toho odkazu co tu daval Gamer na to KDE (diky, super odkaz), tak tohle je proste problem a nejde to. TAKZE CO TED, mam se na to vykaslat? To asi nepujde, musim to nejak vymyslet, ale zase aby to nebylo brutalne neefektivni, protoze to uz bych mohl to delat v Jave (ba ne nemuzu, potrebuju pouzivat Qt).

C++ má na tohle trik: virtuální dědičnost. Pokud nezměníte pořadí již definovaných metod v rozhraních, pak klidně můžete přidávat další. (Alespoň v implementaci GCC + Clang.)

#############################

Vyjimky pouzivat nehodlam, to uz jsem zachytil, ze v C++ je to resene nejak blbe.

Jak blbě?

Myslim, ze jsem je zacal brat jako blbe reseni po precteni tohohle clanku http://250bpm.com/blog:4. Celkove jsem na to ale narazil
uz x-krat, ze jsou proste resene spatne. Konkretne proc si nepamatuju, v C++ jsem zatim skoro nic nenaprogramoval. Mam jen nejake ty teoreticke znalosti z toho co jsem si procital v prubehu zivota, abych nebyl omezenej jen na ty moderni jazyky jako Java.

Chci mit neco jako operator instanceof v Jave. C++ ma dynamic cast a Qt qobject_cast, ale to mi nevyhovuje, protoze dynamic cast je pomalej a qt zase neumoznuje castnout interface zpatky na tu implementujici tridu.

To říká kdo, že je dynamic_cast pomalejší oproti instanceof? V GCC a Clangu je implementovaný prakticky totožně jako v JVM.

dynamic_cast je pomalý, ale to je i instanceof.

To nerikam, ze je pomalejsi nez instanceof, ale kdyz uz neco delam v tom C++, tak bych chtel, aby to bylo efektivnejsi nez treba v Jave.

Na tu virtualni dedicnost kouknnu, ale taky jsem uz na netu narazil na to, ze je to nejaky pomalejsi ta virtualni dedicnost.

bjarne

Re:Pro C++ experty.
« Odpověď #42 kdy: 04. 12. 2015, 17:19:52 »
To jsem mel poresene. V debug nastaveni (tzn. pri vyvoji) je new operator a konstruktory privatni, takze na stacku nikdo nic nevytvori, stejne tak nepouzije normalni new, ale musi pres makro, ktere se nahrazuje volanim metody ve tride, ktera je jako friend.

Hmm, ta knihovna teda ty konstruktory sice bude exportovat, ale přesto by neměly být používány. Takže uživatel dostane speciální makro, které bude při vývoji hlídat, zda to náhodou neudělal. No, pominu-li, že mi to přijde ulítlé, tak jen dodám, aby sis dal pozor na implicitní konstruktory, protože jejich volání jsi nezabránil.

Oni budou pozivane, ale az pri release, pak se to makro totiz preklada na normalni new a ty konstruktory uz nejsou privatni. Takhle tam v release nebude ten overhead spojeny s tim volanim metod na te Memory tride. Proste kdyz programator bude vyvijet v debugu a pak az bude vsechno odladene to jen prelozi jako release, tak to bude v pohode. V debugu to nepovoli ani pouzivat primo new, ani alokovat na stacku. Ale byl to jen nastrel, hodne veci tam urcite jeste neni poresene a jak uz jsem pochopil, tak to stejne bude k nicemu.

Re:Pro C++ experty.
« Odpověď #43 kdy: 04. 12. 2015, 17:24:18 »
V debugu to nepovoli ani pouzivat primo new, ani alokovat na stacku.

Jenže aktuálně ti to alokaci na stacku pořád umožní například přes implicitní copy kontruktor.

bjarne

Re:Pro C++ experty.
« Odpověď #44 kdy: 04. 12. 2015, 17:25:08 »
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


Kód: [Vybrat]
Potomek &p = predek.getIfc<Potomek>()

nebo

Kód: [Vybrat]
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ě lightspeed

https://github.com/ondra-novak/lightspeed/blob/master/src/lightspeed/base/interface.h
https://github.com/ondra-novak/lightspeed/blob/master/src/lightspeed/base/interface.tcc


Díky, trochu se tim prostouram a podivam se, jaky pristup by se mi mohl hodit.