Softwarearchitektur Initialisierung Grafik API mit Handle



  • Hallo alle zusammen 🙂

    Ich habe mir ein Prototyp entwickelt, welcher ein Handle zurückgibt mit verschiedenen Informationen, je nach dem was man für ein GRAPHIC_API Wert angibt. Wenn zum Beispiel DIRECTX angegeben wird, dann wird die Zeile 'DirectX API' ausgegeben. Wenn man jedoch den WERT VULKAN angibt wird die Zeile 'Vulklan API' ausgegeben.
    Das funktioniert auch einwandfrei. Nun hab ich jedoch zwei kleine Fragen.
    Ist diese Art von Klassenarchitektur sinnvoll, oder kann man das besser lösen?
    Handelt es sich dabei eventuell um ein Design Pattern?
    So wie ich gesehen hab, ist es wohl keine Factory, oder?

    typedef void* HANDLE;
    
    class VulkanAPI
    {
    private:
    	std::string name = "Vulklan API";
    public:
    	std::string getName()
    	{
    		return name;
    	}
    };
    
    class DirectXAPI
    {
    private:
    	std::string name = "DirectX API";
    public:
    	std::string getName()
    	{
    		return name;
    	}
    };
    
    enum GRAPHIC_API
    {
    	VULKAN = 0,
    	DIRECTX = 1
    };
    
    class APIConfig
    {
    public:
    	VulkanAPI* apiVulkan = nullptr;
    	DirectXAPI* apiDirectx = nullptr;
    	GRAPHIC_API api;
    
    	std::string GetApi()
    	{
    		if (api == VULKAN)
    		{
    			return apiVulkan->getName();
    		}
    		else if (api == DIRECTX)
    		{
    			return apiDirectx->getName();
    		}
    		else
    		{
    			return "";
    		}
    	}
    };
    
    class InvisionRenderer
    {
    private:
    	APIConfig* config;
    		
    public:
    	InvisionRenderer(GRAPHIC_API api, HANDLE& handle)
    	{
    		config = new APIConfig();
    		if (api == VULKAN)
    		{
    			config->apiVulkan = new VulkanAPI();
    			config->api = VULKAN;
    			handle = (HANDLE)(config);
    		}
    		else if (api == DIRECTX)
    		{
    			config->apiDirectx = new DirectXAPI();
    			config->api = DIRECTX;
    			DirectXAPI* ap = config->apiDirectx;
    			handle = (HANDLE)(config);
    		}
    		else
    		{
    			throw "undefined Handle";
    		}
    	}
    
    	~InvisionRenderer()
    	{
    		if (config->api == VULKAN)
    		{
    			delete config->apiVulkan;
    		}
    		else if (config->api == DIRECTX)
    		{
    			delete config->apiDirectx;
    		}
    
    		delete config;
    	 }
    
    };
    
    class InvisionCommandBuffer
    {
    public:
    	void printSelectedAPI(void* handle)
    	{
    		std::cout << ((APIConfig*)handle)->GetApi() << std::endl;
    	}
    };
    
    void test()
    {
         HANDLE config;
    
         InvisionRenderer *dx = new InvisionRenderer(DIRECTX, config);
         InvisionCommandBuffer commandBuffer;
         commandBuffer.printSelectedAPI(config);
         dx->~InvisionRenderer();
         int test = 0;
    }
    
    int main()
    {
         test();
         return 0;
    }
    

    Gruß

    Dennis



  • @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    Ist diese Art von Klassenarchitektur sinnvoll

    Nein, das ist völliger Käse.

    oder kann man das besser lösen?

    Ja 🙂
    Google mal nach "interface class c++" oder sowas.

    Weiters:

    • Du keine klare Zuständigkeit. Deine "if API so-und-so" Abfragen sind in APIConfig und InvisionRenderer zu finden. Dafür sollte nur eine Klasse zuständig sein. Und idealerweise hast du diese Abfrage überhaupt nur an genau einer Stelle.
    • Du hast das mit delete und den Destruktoren nicht verstanden (dx->~InvisionRenderer(); - WTF?).
    • Du hast das Prinzip der Kapselung nicht verstanden.
    • Was genau versprichst du dir von deinem sog. HANDLE?
    • throw "undefined Handle"; - don't make baby Jesus cry.


  • Das sehe ich so wie @hustbaer.
    Deine Grafikklassen sollten von einer gemeinsamen Interface-Klasse erben, über die dann die gesamte API definiert ist.
    Und bzgl. der anderen Fehler in deinem Code solltest du dich besser ersteinmal mit den Grundlagen von C++ beschäftigen, bevor du dich an Grafikprogrammierung heranwagst.



  • Hallo @hustbaer und @Th69,

    vielen Dank für eure Kritik.
    Ich muss jedoch sagen, dass es mich schon ziemlich traurig macht, dass ich nach ca 10 Jahren Programmieren immer noch nicht richtig hin bekomme.
    So wie ich das verstehe ist meine Architektur totaler Müll. 😞
    Ich verstehe es nicht nach dem ich vor mehreren Jahren Kritik in Bezug mit Zeigeranwendung bekommen hatte, hatte ich mir zwei Bücher zu c++ geholt und die auch weitgehends durch gelesen. Nun wieder so Kritik hat mich ziemlich deprimiert. Ich hab langsam echt das Gefühl, dass ich zu dumm bin. 😞

    Nun zu euren Punkten:
    'Du keine klare Zuständigkeit. Deine "if API so-und-so" Abfragen sind in APIConfig und InvisionRenderer zu finden. Dafür sollte nur eine Klasse zuständig sein. Und idealerweise hast du diese Abfrage überhaupt nur an genau einer Stelle.'
    Hmm ok, ich hatte vor das so zu entwickeln, dass der Benutzer eine Art Config Objekt von Vulkan zurückgegeben bekommt. Jedoch sollte dieses Objekt dem Benutzer verborgen bleiben. Das Config Objekt sollte, dann den Pipeline, CommandBuffer Klassen übergeben werden, da diese alle eine Instanz benötigen.
    Mein Grundziel war es in meiner Grafikengine zwischen DirectX und Vulkan beim Enginestart auszuwählen.

    Du hast das mit delete und den Destruktoren nicht verstanden (dx->~InvisionRenderer(); - WTF?).
    Ich weiß, dass ist totaler Käse und das macht man nicht. Jedoch hatte ich da das Problem, dass am Ende der Destruktor in meinen Programm nicht aufgerufen wurde.
    Aber ja, die Zeile gehört gelöscht.

    Du hast das Prinzip der Kapselung nicht verstanden.
    Ach, du meinst den direkten Zugriff? Also nicht über Getter und Setter, wenn ich das richtig verstehe.
    Sorry, ich hatte vergessen zu erwähnen, dass es sich dabei um einen Prototypen handelt. Ich wollte erst einmal schauen ob die Architektur was taugt.

    Was genau versprichst du dir von deinem sog. HANDLE?
    Ich wollte das config Objekt verbergen. Dieses Objekt soll als eine Art Instanz dienen.

    throw "undefined Handle"; - don't make baby Jesus cry.
    Das ist auch provisorisch, wird natürlich noch durch eine passende Exception ersetzt.

    Deine Grafikklassen sollten von einer gemeinsamen Interface-Klasse erben, über die dann die gesamte API definiert ist.
    Die Objekte haben nur in dem Beispiel die gleichen Methoden. In meiner Engine werden die Methoden der Hintergrundklassen vermutlich abweichen können, da beide API's verschieden aufgebaut sind. Aktuell hab ich nur Vulkan implementiert und mein Ziel ist es, dass die Engine einfach erweiterbar ist um weitere API's.
    Aus diesem Grunde hatte ich das auch über IF Abfragen gelöst.

    Beim beantworten der Fragen ist mir auch aufgefallen, dass ich vergessen hatte zu erwähnen, dass es sich hierbei aktuell noch um einen Prototypen handelt.
    Ich wollte, dass im nächsten Schritt ordentlich in meine Engine implementieren.
    Ich hatte, dass mit den Handles bei einigen API's bereits gesehen und dachte, dass ist eine gute Möglichkeit um benötigte Daten zu übergeben.
    Windows arbeitet ja auch mit Handles, so weit ich weiß.



  • @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    zwei Bücher zu c++ geholt

    Wenn man Architekt werden will reicht es nicht, zig Bücher über Beton gelesen zu haben. Du solltest dich vielleicht auch mal mit Design Pattern (Entwurfsmustern) befassen (der Klassiker: das Gang of Four Buch). Hier wäre das Strategy Pattern eine Überlegung wert.

    @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    Jedoch hatte ich da das Problem, dass am Ende der Destruktor in meinen Programm nicht aufgerufen wurde

    Ja warum auch. new ohne delete!



  • Wie willst du denn eine gemeinsame API anbieten, wenn der Anwender verschiedene Funktionen jeweils aufrufen muß (das ergibt doch dann keinen Sinn)?

    Wo genau möchtest du denn überhaupt bisher die eingestellte Konfiguration setzen (das fehlt bisher komplett)?

    Und bzgl.:

    Jedoch hatte ich da das Problem, dass am Ende der Destruktor in meinen Programm nicht aufgerufen wurde.

    Da du new zum Allozieren benutzt hast, mußt du delete aufrufen! Bei deinem (Test-)Code ist jedoch keine dynamische Allozierung nötig:

    InvisionRenderer dx(DIRECTX, config);
    

    Nach 10 Jahren C++ solltest du das wissen (eigentlich schon nach wenigen Tagen/Wochen)...



  • Puuuuh, das kann jetzt ziemlich langatmig werden. Also wenn ich zeit und Muße hätte, so ein Projekt anzugehen würde ich es vermutlich so machen:
    Zunächst brauchst du eine abstrakte Basisklasse für deine API, von der du dann konkrete Implementationen ableiten kannst. Ich kenne mich mit Grafikprogrammierung nicht aus, deswegen werden meine Beispiele vermutlich Käse sein, aber sie sollen ja auch nur das Prinzip verdeutlichen. Ich beschränke das Beispiel mal auf eine Renderer-Klasse und zwei Engines. Ich benutze std::shared_ptr, das bekommt man aber vllt auch mit std::unique_ptr hin

    // Basisklassen zur Abstraktion von der benutzten Engine

    #include <memory>
    #include <string>
    
    namespace EngineType
    {
       enum Type
       {
          Unknown,
          DirextX,
          Vulkan
       };
    }
    
    class IGraphicsEngine;
    
    // abstrakte Basisklasse für einen Renderer
    class IRenderer
    {
       // erzeugende Engine
       GraphicsEngine* Engine = nullptr;
    
    public:
       IRenderer() = delete;
       IRenderer( IGraphicsEngine* engine )  :
          Engine( engine )
       {
       }
       virtual ~IRenderer() = default();
    
       IGraphicsEngine* engine()
       {
          return Engine_;
       }
    
       // Engine-spezifische Implementation
       virtual void render() = 0;
    };
    
    // abstrakte Basisklasse für die Engine
    class IGraphicsEngine
    {
       EngineType::Type Type_ = EngineType::Unknown;
       std::string const Name_;
       std::string const Version_;
    
    public:
       IGraphicsEngine( EngineType::Type type, std::string const& name, std::string const& version ) :
          Type_( type ),
          Name_( name ),
          Version_( version )
       {
       }
      
       EngineType::Type type() const
       {
          return Type_;
       } 
    
       std::string const& name() const
       {
          return Name_;
       }
    
       std::string const& version() const
       {
          return Version_;
       }
    
       // Erzeugerfunktionen für alle möglichen Objekte, die eine Engine so braucht. 
       virtual std::shared_ptr<IRenderer> create_renderer() const = 0;
       ...
       ...
    };
    

    Implementation für DirectX:

    class DirectXEngine; 
    class DirectXRenderer : public IRenderer
    {
    public:
       // möglicherweise braucht der Renderer noch Engine-spezifische Objekte, um eingesetzt werden
       // zu können. Die könnten dann als zusätzliche Parameter im Konstruktor übergeben werden.
       DirectXRenderer( DirectXEngine* engine ) :
          IRenderer( engine )
       {
       }   
       
       void render() override
       {
          // tu was DirectX-spezifisches
       }      
    };
    
    class DirectXEngine : public IGraphicsEngine
    {
    public:
       DirectXEngine() :
          IGraphicsEngine( EngineType::DirectX, "DirectX", "12.1" ) 
       {
       }
     
       std::shared_ptr<IRenderer> create_renderer() const override
       {
          return std::make_shared<DirectXRenderer>( this );
       }
    };
    

    Implementation für Vulkan:

    class VulkanEngine;
    class VulkanRenderer : public IRenderer
    {
    public:
       // möglicherweise braucht der Renderer noch Engine-spezifische Objekte, um eingesetzt werden
       // zu können. Die könnten dann als zusätzliche Parameter im Konstruktor übergeben werden.
       VulkanRenderer ( VulkanEngine* engine ) :
          IRenderer( engine )
       {
       }   
       
       void render() override
       {
          // tu was Vulkan-spezifisches
       }      
    };
    
    class VulkanEngine : public IGraphicsEngine
    {
    public:
       VulkanEngine () :
          IGraphicsEngine( EngineType::Vulkan, "Vulkan", "1.2.137" ) 
       {
       }
     
       std::shared_ptr<IRenderer> create_renderer() const override
       {
          return std::make_shared<VulkanRenderer >( this );
       }
    };
    

    Und zu guter Letzt noch eine Factory für die Engines. Wobei die Engine-Klassen selbst eigentlich auch wieder Factories sind.

    std::shared_ptr<IGraphicsEngine> create_engine( EngineType::Type type )
    {
       if( type == EngineType::DirectX ) return std::make_shared<DirectXEngine>();
       else if( type == EngineType::Vulkan ) return std::make_shared<VulkanEngine>();
       return std::shared_ptr<IGraphicsEngine>();
    }
    

    Einsatz:

    int main()
    {
        auto engine = create_engine(  EngineType::DirectX );
        auto renderer = engine->create_renderer();
        renderer->render();
    }
    

    Ist auf jeden Fall ein Haufen Arbeit, um den ich mich nicht reißen würde.
    Ist so runtergeschrieben, also kein Anspruch auf Übersetzbarkeit.



  • @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    Ich hatte, dass mit den Handles bei einigen API's bereits gesehen und dachte, dass ist eine gute Möglichkeit um benötigte Daten zu übergeben.Windows arbeitet ja auch mit Handles, so weit ich weiß.

    Dann sollte man aber auch verstanden haben, was Handles sind und wie das OS sie benutzt. Du missbrauchst sie einfach als void*, und das ist nicht der richtige Anwendungsfall.



  • @Th69
    Diese Klassen VulkanAPI und DirectXAPI sind Wrapper Klassen, die haben teilweise unterschiedliche Konzepte nenne ich mal das.
    In Vulkan muss man ziemlich viel selbst machen, was in DX 11 zum Beispiel nicht so kompliziert ist, so wie ich das mal gelesen hatte.
    Nun hatte ich um die Vulkan Klassen einen Wrapper entwickelt, welche der Klasse VulkanAPI entspricht.
    Genauso soll es irgendwann eine Wrapper Klasse um DirectX geben, welche in dem Prototyp DirectXAPI heist.
    Das Ziel ist es nun die Funktionalitäten zu vereinheitlichen in Klassen wie Renderer, CommandBuffer, Pipeline, weil alle API's nach dem gleichen Prinzip arbeiten.
    Jedoch muss je nach API mehr oder weniger dynamisch konfiguriert werden.

    Vielen Dank für deinen Hinweis:
    Ehrlich gesagt, hatte ich das Delete zu der Zeile 'InvisionRenderer *dx = new InvisionRenderer(DIRECTX, config);' vergessen.
    Also ich weiß schon, dass es sich bei new und delete um eine Art der dynamischen Speicherallokation handelt.
    Ich hatte halt einfach vergessen das delete aufzurufen.
    Jetzt merke ich auch, dass das der Grund war, weshalb der Destruktor nicht aufgerufen wurde. 😩

    @manni66
    Vielen Dank, für deinen Hinweis. 🙂
    Das vergessene delete war das Problem, weshalb der Destruktor nicht aufgerufen wurde.
    Ich werde mir das Strategy Pattern mal näher anschauen. 🙂
    Des Weiteren werde ich mich auch mal mehr mitn Software Architektur und Design Patterns beschäftigen, weil da hab ich noch so meine Schwächen.

    @DocShoe
    Wenn ich das richtig verstehe erzeuge ich dann auch CommandBuffer, die Pipeline etc. über das engine Objekt, oder?
    Hmm, das werde ich mir nochmal genauer anschauen und ausprobieren.
    Ich merke wirklich, dass es mir an Software Architektur und Design Patterns noch mangelt.
    Danke @manni66 da für deinen Hinweis. Ich werde mich in diese Gebiete auch nochmal reinfuchsen.

    @DocShoe
    Hmm, dann werde ich mich da nochmal reinlesen müssen.
    Weil, ehrlich gesagt dachte ich, dass ich handles nutzen könnte um einen Pointer zu übergeben.
    Ich dachte einfach Handle klingt aussagekräftiger als void*, da wird ersichtlich, dass es eine Art Konfigurationsobjekt ist.





  • Was die Sportfreunde mit Interface-Klasse meinten ist folgendes:

    class APIInterface
    {
    public:
         virtual const std::string &getName() const = 0;
         virtual void doStuff() = 0;
    }
    
    class DirectXAPI : public APIInterface
    {
    public:
        virtual const std::string &getName() const override { return "DirectXAPI"; }
        virtual void doStuff() override { /* do DirectX-Shit */ }
    }
    
    class VulkanAPI : public APIInterface
    {
    public:
        virtual const std::string &getName() const override { return "VulkanAPI"; }
        virtual void doStuff() override { /* do Vulkan-Shit */ }
    }
    

    D.h. es gibt eine BasisKlasse, die alle benötigten Funktionen anbietet, aber nicht implementiert.
    Und darüberhinaus gibt es Klassen, die von dieser Basisklasse ableiten und dann die eigentliche API-spezifische Funktionalität hinterlegen. Eigentlich perfekt für deinen Anwendungsfall, WENN die beiden APIs überhaupt vereinheitlicht werden können.

    Was genau du mit dem Handle tun willst, ist mir nicht klar. Mir scheint aber, das wäre ein guter Anwendungsfall für eine Art Singleton bzw. für ein statisches Objekt, welches vom Typ der Basisklasse ist ( Interface ) und wo im Grunde für den Anwender unerheblich ist, was wirklich dahintersteckt. Der Nutzer arbeitet dann nur mit diesem Singleton.

    Ich hoffe ich habe verstanden worauf du hinaus willst 😉



  • @It0101 und @DocShoe
    ich will nun die Architektur von @DocShoe nutzen, da werden Interfaces benutzt.
    Das ähnelt ja auch soweit deinen Vorschlag @It0101, wenn ich das richtig sehe.
    Ich merke, in dem Bereich Polymorphie und Software Architektur hab ich noch so leicht meine Schwächen.
    So wie ich das verstehe, scheint es sich dabei um ein Strategie Pattern in Kombination eines Factory Patterns zu handeln.

    @It0101
    Das Singleton ist eins der wenigen Design Pattertns, die ich bereits kenne.
    Jedoch hab ich da mal gelesen, es wäre ein Anti Pattern. Seit dem hab ich das Design Pattern nicht mehr verwendet.
    Andererseite würde es für einen Renderer ja Sinn machen, aber wie gesagt bin mir da total unsicher, weil viele sagen man solls nicht verwenden, während andere sagen, man kann es verwenden.

    Jetzt bin ich an dem Punkt, dass ich ein Problem mit Shared_Ptr hab. So wie ich das verstehe, können Shared_Ptr an eine andere Instanz übergeben werden, während Unique_Ptr nicht an eine andere Instanz übergeben werden können.
    Jedoch bereiten mir noch zwei Fehlermeldungen Probleme.
    'return': cannot convert from 'std::shared_ptr<VulkanRenderer>' to 'std::shared_ptr<IRenderer>' und
    'return': cannot convert from 'std::shared_ptr<DirectXRenderer>' to 'std::shared_ptr<IRenderer>'. Ich verstehe nicht so ganz wieso das nicht funktioniert, weil beide Klassen VulkanRenderer und DirectXRenderer sind doch Kindklassen von IRenderer.

    class IRenderer
    {
    	// erzeugende Engine
    	IGraphicsEngine* Engine = nullptr;
    
    public:
    	IRenderer() = delete;
    	IRenderer(IGraphicsEngine* engine) :
    		Engine(engine)
    	{
    	}
    	virtual ~IRenderer() = default;
    
    	IGraphicsEngine* engine()
    	{
    		return Engine;
    	}
    
    	// Engine-spezifische Implementation
    	virtual void render() = 0;
    };
    
    class DirectXEngine : public IGraphicsEngine
    {
    public:
    	DirectXEngine() :
    		IGraphicsEngine(EngineType::DirectX, "DirectX", "12.1")
    	{
    
    	}
    
    	std::shared_ptr<IRenderer> create_renderer() const override
    	{
    		return std::make_shared<DirectXRenderer>(this);
    	}
    };
    
    class VulkanEngine : public IGraphicsEngine
    {
    public:
    	VulkanEngine() :
    		IGraphicsEngine(EngineType::Vulkan, "Vulkan", "1.2.137")
    	{
    		
    	}
    
    	std::shared_ptr <IRenderer> create_renderer() const override
    	{
    		return std::make_shared<VulkanRenderer >(this);
    	}
    };
    

    ich merke nun, dass ich mich noch einmal in die Themen Polymorphie und Smart Pointers einlesen sollte.
    Das sind wohl bei der Nutzung von Design Pattern standard Techniken.



  • @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    'return': cannot convert from 'std::shared_ptr<VulkanRenderer>' to 'std::shared_ptr<IRenderer>' und
    'return': cannot convert from 'std::shared_ptr<DirectXRenderer>' to 'std::shared_ptr<IRenderer>'. Ich verstehe nicht so ganz wieso das nicht funktioniert, weil beide Klassen VulkanRenderer und DirectXRenderer sind doch Kindklassen von

    Prüfe mal deine Klassendefinition von Vulkanrenderer. Theoretisch sollte irgendwo stehen:

    class VulkanRenderer : public IRenderer
    {
    }
    


  • @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    class DirectXEngine : public IGraphicsEngine

    Ich verstehe nicht so ganz wieso das nicht funktioniert, weil beide Klassen VulkanRenderer und DirectXRenderer sind doch Kindklassen von IRenderer.

    Offenbar nicht, DirectXEngine erbt von IGraphicsEngine und nicht von IRenderer

    Edit: zu langsam



  • @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    std::shared_ptr<IRenderer> create_renderer() const override

    Ich würde einen Fehler durch das const erwarten.

    Du zeigst keine vollständigen Fehlermeldungen!



  • @manni66 sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    std::shared_ptr<IRenderer> create_renderer() const override

    Ich würde einen Fehler durch das const erwarten.

    Du zeigst keine vollständigen Fehlermeldungen!

    Warum sollte das const ein Problem sein?

    Das "const" sagt ja nur: Diese Funktion verändert nicht das Klassen-Objekt selbst. Das tut die Funktion auch nicht.



  • @It0101 sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    @manni66 sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    std::shared_ptr<IRenderer> create_renderer() const override

    Ich würde einen Fehler durch das const erwarten.

    Du zeigst keine vollständigen Fehlermeldungen!

    Warum sollte das const ein Problem sein?

    Das "const" sagt ja nur: Diese Funktion verändert nicht das Klassen-Objekt selbst. Das tut die Funktion auch nicht.

    Was erwartet IRenderer im Konstruktor?



  • @manni66 sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    @It0101 sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    @manni66 sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    @Pixma sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    std::shared_ptr<IRenderer> create_renderer() const override

    Ich würde einen Fehler durch das const erwarten.

    Du zeigst keine vollständigen Fehlermeldungen!

    Warum sollte das const ein Problem sein?

    Das "const" sagt ja nur: Diese Funktion verändert nicht das Klassen-Objekt selbst. Das tut die Funktion auch nicht.

    Was erwartet IRenderer im Konstruktor?

    Er übernimmt eine Kopie einer "Zahl" ( den this-Pointer) um es mal ganz plump zu sagen. Und weiter?



  • @It0101 sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    Er übernimmt eine Kopie einer "Zahl" um es mal ganz plump zu sagen

    Plump und falsch.



  • @DocShoe sagte in Softwarearchitektur Initialisierung Grafik API mit Handle:

    Ist so runtergeschrieben, also kein Anspruch auf Übersetzbarkeit.

    Duckundwech...


Anmelden zum Antworten