Neues C++ Tool: Header Hero



  • Kein Pimpler schrieb:

    Daher die Frage, ist das pimpl Idiom mittlerweile obsolet?

    Pimpl zum Sparen von Compilezeit war schon immer eine Schnapsidee.
    Pimpl zum geheimhalten der Implementierung war schon immer eine Schnapsidee.



  • Das begründe mal.



  • Tyrdal schrieb:

    Das begründe mal.

    Muß ich nicht. Ich mußte auch nicht begründen, warum Struktogramme nichts bringen und warum ungarische Notation nichts bringt und warum UML als Planungswerkzeug nichts taugt. Ich lasse mich nicht anstecken, gebe gelegentlich ein lästerliches Statement ab, und warte, bis der Hype von selbst verschwindet.



  • Stimmt du mußt nicht, aber so bist du im Moment ein Troll.



  • Ein Troll der meiner Meinung nach Recht hat. 😉
    Man muss nicht für alles eine Begründung liefern, wenn die Begründung klar auf der Hand liegt.



  • Ich sehe die Begründung nicht. Bitte um eine Erklärung.



  • Das langt für mich :

    More work for the implementor.
    Doesn't work for 'protected' members where access by subclasses is required.
    Somewhat harder to read code, since some information is no longer in the header file.
    Run-time performance is slightly compromised due to the pointer indirection, especially if function calls are virtual (branch prediction for indirect branches is generally poor).



  • Man kann damit unportablen Code "verstecken", sodass zum Beispiel der Header nicht den globalen Namespace mit der WinAPI verseucht. Bei solchen Dingen spielt der Overhead dann auch gar keine Rolle, weil die darunterliegenden Aufrufe relativ teuer sind.

    //event.hpp
    
    struct Event
    {
    	Event();
    	~Event();
    	void set();
    	void wait();
    
    private:
    
    	struct EventImpl;
    
    	std::unique_ptr<EventImpl> m_impl;
    };
    
    //event.cpp
    
    #include "event.hpp"
    
    #ifdef _WIN32
    #include <Windows.h>
    
    struct Event::EventImpl
    {
    	HANDLE eventHandle;
    
    	EventImpl()
    		: eventHandle(::CreateEvent(NULL, FALSE, FALSE, NULL))
    	{
    	}
    
    	~EventImpl()
    	{
    		::CloseHandle(eventHandle);
    	}
    
    	void set()
    	{
    		::SetEvent(eventHandle);
    	}
    
    	void wait()
    	{
    		::WaitForSingleObject(eventHandle, INFINITE);
    	}
    };
    #endif
    
    Event::Event()
    	: m_impl(new EventImpl)
    {
    }
    
    Event::~Event()
    {
    }
    
    void Event::set()
    {
    	m_impl->set();
    }
    
    void Event::wait()
    {
    	m_impl->wait();
    }
    

    EDIT: wie immer ohne Gewähr



  • Mann, ist das schön wenn man in einer Sprache mit Modulen programmiert *troll-modus* 🤡



  • Man muß es nicht begründen, weil derjenige, der was Umständliches einführt, im Begründungszwang ist.

    Ich erfinde mal ein rosa unsichtbares Einhorn. Und dann kommt von mir nachgeschlagen, daß ich jeden, der das Einhorn ablehnt, ohne die Ablehnung zu begründen, ein Troll wäre. Das ist schlecht. Das machst Du aber gerade.

    Wenn Du Pimpl liebst, darfste es begründen.



  • TyRoXx schrieb:

    Man kann damit unportablen Code "verstecken", sodass zum Beispiel der Header nicht den globalen Namespace mit der WinAPI verseucht. Bei solchen Dingen spielt der Overhead dann auch gar keine Rolle, weil die darunterliegenden Aufrufe relativ teuer sind.

    //event.hpp
    
    struct Event
    {
    	Event();
    	~Event();
    	void set();
    	void wait();
    	
    private:
    
    	struct EventImpl;
    	
    	std::unique_ptr<EventImpl> m_impl;
    };
    
    //event.cpp
    
    #include "event.hpp"
    
    #ifdef _WIN32
    #include <Windows.h>
    
    struct Event::EventImpl
    {
    	HANDLE eventHandle;
    	
    	EventImpl()
    		: eventHandle(::CreateEvent(NULL, FALSE, FALSE, NULL))
    	{
    	}
    	
    	~EventImpl()
    	{
    		::CloseHandle(eventHandle);
    	}
    	
    	void set()
    	{
    		::SetEvent(eventHandle);
    	}
    	
    	void wait()
    	{
    		::WaitForSingleObject(eventHandle, INFINITE);
    	}
    };
    #endif
    
    Event::Event()
    	: m_impl(new EventImpl)
    {
    }
    
    Event::~Event()
    {
    }
    
    void Event::set()
    {
    	m_impl->set();
    }
    
    void Event::wait()
    {
    	m_impl->wait();
    }
    

    EDIT: wie immer ohne Gewähr

    Geht viel performanter und meiner Meinung nach auch einfacher, wenn man die WinAPI-Funktionen und -Typen plattformneutral verpackt. Pimpl wird hier mißbraucht. Vermutlich wie die Singhletons, die ebenfalls zu berühmt sind, und sich deswegen auch in unpassende Löcher stopfen.



  • volkard schrieb:

    TyRoXx schrieb:

    ...

    Geht viel performanter und meiner Meinung nach auch einfacher, wenn man die WinAPI-Funktionen und -Typen plattformneutral verpackt. Pimpl wird hier mißbraucht. Vermutlich wie die Singhletons, die ebenfalls zu berühmt sind, und sich deswegen auch in unpassende Löcher stopfen.

    Habe ich nicht genau das gemacht? Wie würdest du es denn viel performanter machen?



  • os/Windows.hpp

    namespace os{
    typedef void* Event;
    Event eventCreate();
    void eventClose(Event event);
    void eventSet(Event event);
    void eventWait(Event event);
    }//namespace os
    

    os/Windows.cpp

    #include <windows.h>
    #include <Windows.hpp>
    
    namespace os{
    static_assert(tmp::isSameType<Event,HANDLE>::value,"Type Mismatch");
    Event eventCreate(){
       return CreateEvent(NULL, FALSE, FALSE, NULL);
    }
    //usw
    }//namespace os
    

    Die Event-Klasse wird dann ganz herkömmlich gebaut, nur halt mit den plattformneutralen API-Namen.

    Zusatzkosten: Nur ein Funktionsaufruf pro API-Aufruf. Und selbst das kann man leicht ifdeffen, so daß im Releasecode wieder lange Compilezeit und zero Laufzeitoverhead hat. Pimpl kann man nicht leicht ifdeffen.



  • Und wenn sich das typedef für HANDLE ändert, musst du deines auch ändern?



  • 314159265358979 schrieb:

    Und wenn sich das typedef für HANDLE ändert, musst du deines auch ändern?

    Ich verstehe die Frage nicht.

    Wieso sollte sich was bei HANDLE ändern? Aber klar, wenn man statt HANDLE jetzt HUHN verwenden muss, muss man das natürlich im Code anpassen. Aber das muss man immer - egal welches Design man verwendet.

    Nur hier muss man es nur an einer zentralen Stelle.



  • Genau dafür ist das typedef in der WINAPI doch da - dass man _nicht_ void* typedeft.



  • 314159265358979 schrieb:

    Genau dafür ist das typedef in der WINAPI doch da - dass man _nicht_ void* typedeft.

    Du hängst dich gerade an Belanglosigkeiten auf.
    ob man jetzt HANDLE oder void* schreibt ist ziemlich egal.

    volkard hat das in dem Code gemacht um den windows Header nicht überall einbinden zu müssen.



  • Shade Of Mine schrieb:

    314159265358979 schrieb:

    Und wenn sich das typedef für HANDLE ändert, musst du deines auch ändern?

    Ich verstehe die Frage nicht.

    Wieso sollte sich was bei HANDLE ändern? Aber klar, wenn man statt HANDLE jetzt HUHN verwenden muss, muss man das natürlich im Code anpassen. Aber das muss man immer - egal welches Design man verwendet.

    Nur hier muss man es nur an einer zentralen Stelle.

    Klar, nur wenn HANDLE nicht mehr void* sondern HUHN ist, dann wird es ohne Pimpl schwierig werden, die HUHN Definition, und damit die <windows.h>, aus dem Header herauszuhalten.



  • Tachyon schrieb:

    Klar, nur wenn HANDLE nicht mehr void* sondern HUHN ist, dann wird es ohne Pimpl schwierig werden, die HUHN Definition, und damit die <windows.h>, aus dem Header herauszuhalten.

    Falls das jemals passiert, kann man sich dann immernoch Gedanken machen. Es ist nicht so, daß Gefahr bestünde, Lösungen seien schwierig zu finden und würden uns tagelang aufhalten.



  • Tachyon schrieb:

    Klar, nur wenn HANDLE nicht mehr void* sondern HUHN ist, dann wird es ohne Pimpl schwierig werden, die HUHN Definition, und damit die <windows.h>, aus dem Header herauszuhalten.

    Da muss man aber ganz schön viel im Voraus pimpern, äh pimpl einsetzen, nur damit man so eine potentielle Situation dann lösen kann. Rechtfertigt den Stress imo nicht.


Anmelden zum Antworten