Objektmanagement und vermeidung globaler Variablen



  • Hallo,

    EDIT:
    Da fällt mir ein, dass ich mich noch gar nicht vorgestellt habe:

    Mein Name ist Mirko und studiere Mechatronik und Flugzeugsysteme im 6. Semester.

    ich arbeite mich gerade mit c++ ein und versuche ein etwas größeres Programm zu bauen.
    Das Programm besteht aus:
    Klassen:

    CFruit(...)
    CApfel:public Cfruit(...)
    CBirne:public Cfruit(...)
    CAuto(...)

    Routinen:

    Plugin.cpp welches ohne Main ist, aber 10 Funktionen hat.

    Nun habe ich überlegt ob ich nicht eine globale Klasse OBJECTS baue, welche die erforderlichen Instanzen der jeweiligen Klasse (CApfel, CBirne..) in std::maps<int,void*>(bspw...)
    speichert und ich in jeder
    Plugin::Funktion()
    über diese globale Instanz OBJECTS auf die maps zugreife.

    globalClass()
    {
    // befüllen der map mit versch. Objekten
    }
    
    OBJECTS globalClass();
    
    Plugin::funktion(){
    globalClass.Zugriffaufmap.Apfelessen; (bspw.) 
    }
    

    Wäre das eine geschickte Lösung um einen Zugriff in der Plugin.cpp auf alle Objekte haben zu können, ohne jedes einzeln global zu definieren?
    Ich habe mal was von Singletones gehört, aber verstehe diese nicht ganz und kann mir natürlich keinen reim darauf machen ob es denn für mich passt


  • Mod

    😮

    Wie wäre es mit einer genau definierten Schnittstelle, anstelle des Chaos, in das du dich hier kopfüber stürzt?



  • MightyMirko schrieb:

    Wäre das eine geschickte Lösung

    Nee.



  • also normalerweise bindet man die jeweiligen methoden in die klassen ein, instanziiert die objekte in der main und übergibt sie dann bei bedarf, da ist nichts global.



  • HansKlaus schrieb:

    also normalerweise bindet man die jeweiligen methoden in die klassen ein, instanziiert die objekte in der main und übergibt sie dann bei bedarf, da ist nichts global.

    Ich habe keine main Funktion. Ich habe lediglich fünf Schnittstellen_Funktionen für das Spiel und fünf eigene CallBacks
    10/10 Funktionen greifen auf jede erstellte Instanz jedes Objektes zu (ca 50-150 Objekte)

    SeppJ schrieb:

    😮

    Wie wäre es mit einer genau definierten Schnittstelle, anstelle des Chaos, in das du dich hier kopfüber stürzt?

    Die Klasse Objects ist eben als Schnittstelle gedacht. So stelle ich mir das vor

    Objects ->{4 Autos mit je {15 Äpfel, 10 Birnen, 4 Bananen}}// Früchte erben von abstrakter Basisklasse mit Polymorphie von CFruit
    

    EDIT: Das hier wären die Schnittstellen-funktionen:

    PLUGIN_API int XPluginStart ( char * outName, char * outSignature, char * outDescription ); 
    PLUGIN_API void XPluginStop ( void ); 
    PLUGIN_API void XPluginEnable ( void ); 
    PLUGIN_API void XPluginDisable ( void ); 
    PLUGIN_API void XPluginReceiveMessage ( XPLMPluginID inFrom, int inMessage, void * inParam );
    

    Dann habe ich noch 5 CallBacks welche vom Spiel selbst zu bestimmten Frames aufgerufen werden:
    Das Spiel wird somit zur Main und ich biete dieser Main nur weitere Funktionen zum aufrufen.

    myDrawCallBack(...)
    .
    .
    

    TANTE_EDIT2:
    Meiner Meinung habe ich 2 Alternativen:

    Anlegen globaler Instanzen
    Eine globale Objects-Klasse welche die Klassen unter sich managt und in dessen scope behält.

    Die erste wäre sehr unschön
    Bei der zweiten bin ich mir sicher, dass es schönere Lösungen gibt. Mein Betreuer hat mir den Hinweis auf Singletone gegeben ( Ich bin Praktikant und kümmere mich um einen Simulator )

    Da fällt mir ein, dass ich mich noch gar nicht vorgestellt habe:

    Mein Name ist Mirko und studiere Mechatronik und Flugzeugsysteme im 6. Semester.



  • Was ich also brauche ist ein Objekt, welches auch außerhalb des Scopes bekannt ist.
    Static bleibt zwar bestehen, bei verlassen des scopes, aber andere scopes sehen diesen nicht.



  • MightyMirko schrieb:

    Was ich also brauche ist ein Objekt, welches auch außerhalb des Scopes bekannt ist.

    Nein. Was du brauchst ist ein besseres API-Design. Davon abgesehen...

    MightyMirko schrieb:

    Static bleibt zwar bestehen, bei verlassen des scopes, aber andere scopes sehen diesen nicht.

    Dann nimm halt ne globale Instanz. Oder ne Funktion die ne Referenz auf ne (Function-Local-)Static zurückgibt. Ist jetzt echt keine Rocket-Science...



  • hustbaer schrieb:

    Dann nimm halt ne globale Instanz. Oder ne Funktion die ne Referenz auf ne (Function-Local-)Static zurückgibt. Ist jetzt echt keine Rocket-Science...

    Ich versuche rauszufinden was das beste ist und ob es Sinn macht ein Singleton-design Pattern anzuwenden bzw eine globale Instanz. Die Anforderungen meines Arbeitgebers sind " keine globalen Variablen". Somit versuche ich diese auch zu vermeiden.

    Rocket Science hin oder her:

    ...ich arbeite mich gerade mit c++ ein und versuche ein etwas größeres Programm zu bauen. ....

    so ne pampige Antwort hat, meiner Meinung nach, in einem gutem Forum nichts verloren

    EDIT:
    test.hpp

    #pragma once
    
    #include <stringstream>
    
    class fruit {
      fruit() = 0;
      virtual ~fruit();
      std::stringstream farbe;
    }
    class apfel{
        apfel();
        ~æpfel();
      }
    class birne {
        birne();
        ~birne();
      } 
    class fahrzeug {
        fahrzeug();
        ~fahrzeug();
        std::map<int, birne *> kisteBirne;
        std::map<int, apfel *> kisteAppel;
      } 
    class objekte {
        objekte();
        ~objekte();
        std::map<int, fahrzeug *> gueterWaggon;
      }
    

    test.cpp

    #include <iostream>
    #include <map>
    #include "test.hpp"
    
    apfel::apfel() : public fruit() { farbe << "rot" }
    apfel::~apfel() {}
    }
    class birne : public fruit() {
      birne::birne() { farbe << "gelb" }
      birne::~birne()
    } 
    class fahrzeug {
    
    fahrzeug::fahrzeug() 
    {
     farbe << "schwarz";
     for (int i = 0; i < 10; ++i)
     {
      birne *tmpBirne = new birne();
      apfel *tmpApfel = new apfel();
      kisteBirne[i] = tmpBirne;
      kisteAppel[i] = tmpApfel;
      }
    }
      fahrzeug::~fahrzeug()
    
    }
    
    objekte::objekte()
    {
      for (int i = 0; i < 10; ++i)
      {
        fahrzeug *tmpAuto = new fahrzeug();
        gueterWaggon[i] = tmpAuto;
      }
    }
    ~objekte {}
    }
    
    #include "test.hpp"
    
    // 1. Möglichkeit
    extern objekte* globClass;
    
    // 2. Möglichkeit
    objekte* singleton = objekte::getInstance();
    /*.
    /*.
    /*! SDK API
    .*/
    .*/
    funktion1(apfel& instance)
    funktion2(birne& instance)
    funktion3(fahrzeug& instance)
    funktion4(apfel& instance,fahrzeug& instance )
    funktion5(apfel& instance,fahrzeug& instance,birne& instance)
    // keine Main
    


  • Der Sinn des Singleton ist nicht der, den du anstrebst...

    https://www.c-plusplus.net/forum/340861


  • Mod

    Zusammengefasst: Du machst also dein erstes größeres Programm; läufst direkt in große Designprobleme; aber wenn man dir sagt, dass du ein besseres Design brauchst anstatt dein bisheriges Design zu retten, dann ist das eine pampige Antwort. Ja, nee, ist klar...

    Ich versuche rauszufinden was das beste ist und ob es Sinn macht ein Singleton-design Pattern anzuwenden bzw eine globale Instanz.

    Das Konzept "beste Lösung" wird praktisch nie mit "globale Instanz" oder "Singleton" in Verbindung gebracht, außer dazwischen taucht eine Verneinung auf.

    Entschuldigung für die pampige Antwort.



  • @MightyMirko
    Du willst die Semantik einer globalen Variable verwenden ohne ne globale Variable zu verwenden. Jenachdem wie man den Begriff "globale Variable" versteht lässt sich das schon machen. Bloss dass es keinen Sinn macht.

    Der Grund dass dein Auftraggeber keine globalen Variablen haben möchte ist nicht weil er die Bezeichnung "globale Variable" phöse findet, sondern weil er kein schrottiges Design möchte.

    Und was die "pampige" Antwort angeht... also, was erwartest du dir wenn einer ankommt und sagt er will so einen auf globale Variable machen aber halt ohne globale Variablen. Darfuque?



  • Ok.. Entschuldigt bitte, war selbst pampig..

    Für das API-Design bin ich nicht verantwortlich( XPlane SDK - Sandy Barbour C 2008). Ich versuche mich nur dem zu richten was gefordert wird.
    Global,callby-ref/val(was nicht funktioniert) und Singleton sind die Dinge die ich kenne. Andere DesignPatterns kannte ich bis heute morgen auch noch nicht.

    Durch das Topic erhoffte ich mir ein paar Tipps/Tricks/Stichworte damit ich das Design echt "smell"-less machen kann, denn ich will ja ne gute Leistung erbringen.

    http://www.xsquawkbox.net/xpsdk/mediawiki/Category:Documentation

    Ein bisschen Infos zur Arbeit:

    Ich soll ein Plugin für X-Plane entwickeln mit ROS-, MatLab-, Python-, OpenStreetMap-Schnittstellen. X-Plane stellt verschiedene, selbstgemachte Objekte(Satelliten, Flugzeuge, Drohnen) dar. Damit schaff ich eine grafische Simulationsumgebung für Luftverkehrskontrolle. Die Physik wird in MatLab/Simulink berechnet.

    Einige Beispiele:

    https://github.com/nasa/XPlaneConnect
    https://github.com/Davidbrcz/hla-applications
    https://github.com/dmarc0001/SaitekX52MFD

    <<-- Der dmarc0001 hatte nen ähnlichen Ansatz, mit der Deklarierung einer globalen pmCL Klasse



  • wie gesagt: wenn du von deinen objekten auf ein anderes zugreifen möchtest, dann indem du entweder den methoden eine referenz übergibst, oder (besser) indem du in dem objekt selbst direkt eine referenz vorhälst, d.h. dem konstruktor diese referenz übergibst.

    wenn du jetzt selbst keine main hast, bedeutet das ja nicht, dass es nicht irgendwo eine gibt.



  • Ok, das ist eine vorgegebene Plugin Schnittstelle. Ich sehe da auf die Schnelle leider auch keine Möglichkeit, die Daten sauber zu verwalten.



  • Man war ich schon ewig nicht mehr hier im Forum.

    Mirko, das hauptsächliche Problem an der Plugin Schnittstelle ist, dass es eine C Schnittstelle ist.
    C++ Callback Schnittstellen sehen ein bisschen anders aus und können dann auch Klassenfuntionen sein. Wenn du u.a. mit ROS arbeitest, kannst du da ja mal rein schaun, da werden solche Konstrukte verwendet.

    Das Beispiel, dass du von dmarc0001 verlinkt hast ist vielleicht ein ganz netter Ansatz.

    Da das Interface, wie bei C-Schnittstellen nicht unüblich, die Datenkommunikation mit void-Pointern realisiert müssen die relevanten Daten in einer Klasse oder einem Struct gekapselt sein.
    Auf dieser Datenstruktur kann dann gearbeitet werden. Aber die kommt jeweils aus der Callbackfuntion. Da wird keine globale Klasse parallel mit Daten befüllt, sondern halt immer auf der selben Instanz der einen Klasse gearbeitet, die in XPluginStart() instantiert worden ist.

    Also kannst du eine "Working Class" erstellen:

    class workingClass{
    CFruit m_fruit;
    CApfel m_apple;
    CBirne m_pear;
    CAuto m_car;
    }
    

    und diese Klasse dann ann deine registrierten Callbacks übergeben:

    workingClass *testClass = new WorkingClass(...);
    XPLMRegisterFlightLoopCallback(myCallbackmyfLoopCallback,1.0,static_cast<void *>(testClass) );
    

    Ich hoffe, ich konnte ein bisschen zu deinem Verständnis beitragen.


Log in to reply