Macro + UpperChar



  • Hallo, kann ich in einem Macro wie diesem

    #define GETTER(type, name) \
    	type [b]n[/b]ame() const { return name; }
    

    Den ersten Buchstaben des in name übergebenen Strings in einen Großbuchstaben ändern? Wenn ja wie?

    Wenn ich also

    GETTER(int, integer)
    

    aufrufe, sollte mir der Prozessor

    int Integer() const { return integer; }
    

    daraus machen.



  • Ungeachtet der eigentlich Frage: Don't!


  • Mod

    Nein, das ist nicht möglich.

    Und ich hake lieber nicht nach, was das werden soll, das ist schlecht für meinen Blutdruck...



  • Ich sage es dir trotzdem ;). Ich möchte wie man unschwer erkennen kann den Setter und Getter automatisch durch ein Macro erstellen können. Und da ich dieses Set & Get nicht mag und

    int integer;
    int integer() {return integer};
    

    nicht funktioniert, soll der Anfangsbuchstabe der Funktionen groß geschrieben werden.


  • Mod

    😡 Es gibt Dinge, die will man gar nicht wissen. Jetzt kann ich mir nicht mehr einreden, dass dein erster Beitrag nur ein Beispiel gewesen sein könnte und du das gar nicht wirklich machen wolltest.

    Und irgendwie sollte ich mich jetzt verpflichtet fühlen, etwas über Makros und Kapselung zu erzählen. Fühle ich mich aber nicht, denn ich bin müde, jemand anderes wird das sicher gleich tun.



  • Das Interessiert mich jetzt aber. Was haben denn Macros mit Kapselung zu tun? Ich dachte die ersetzen nur stupide den Text?



  • Hör auf Decimad und SeppJ. Warum?


  • Mod

    Student83 schrieb:

    Das Interessiert mich jetzt aber. Was haben denn Macros mit Kapselung zu tun? Ich dachte die ersetzen nur stupide den Text?

    Es grht mir darum, dass du gleich zwei Sünden auf einmal begehst. Automatische Codegenerierung ist ja sehr hübsch, aber du solltest dir klar sein, dass Makros jede Menge Nachteile haben.

    Das andere ist, wenn jemand so viele triviale Getter/Setter schreibt, dass er ein Makro dafür will, dann stimmt etwas am Design nicht. Hier ein alternativer Vorschlag, der das gleiche erreicht:

    // Vor allen Includes
    #define class struct
    #define private public
    

    (Es gibt 2 Arten von Menschen: Solche die obiges als gute Lösung für ihre Probleme ansehen und solche die obiges als Hinweis auf die Schwächen ihres Design verstehen)



  • Und solche, die ihm trotzdem nicht die Lösung vorenthalten. Ob er sich für sein konkretes Problem zu einer Alternative überreden lässt, bleibt davon ungeachtet.

    #define GETTER(X,Y)\
      X Y;\
      X Get_##Y() const
    
    struct Foo {
      GETTER(int, integer);
    };
    
    int main()
    {
      Foo foo;
      foo.Get_integer();
    }
    

    Man kann aber gleich verschiedene Probleme dabei erkennen. Der Zugriff auf die Variablen lässt sich nur schwer einschränken, weil der vorherige Zustand nicht Wiederherstellbar ist. Und man kann nur schwer erkennen, wie denn die konkrete Get-Methode nun eigentlich heißt.



  • mixxa schrieb:

    Und man kann nur schwer erkennen, wie denn die konkrete Get-Methode nun eigentlich heißt.

    hehe^^ und intellisense funktioniert nicht 😃

    d.h. --->>> man muss doch wieder mehr selbst schreiben



  • @mixxa
    Genau dieses Get/Set will ich ja nicht, es sollte dann schon

    int main()
    {
      Foo foo;
      foo.Integer();
    }
    

    heißen. Aber ihr habt mich überredet eingentlich brauche ich an der Stelle gar keine Macros und werde alles selber Tippen.



  • Ergänzend: Das Problem aus meiner Sicht mit den ganzen gettern/settern ist, dass man dann dazu neigt, die eigentliche Funktionalität außerhalb der Klasse anzusiedeln; die dient dann nämlich nur noch als quasi-public Datenhaltungsobjekt (aka struct). Ich kenne sowas eher von Java, kann aber nicht beurteilen, inwiefern das da mehr Sinn macht.

    Im Allgemeinen sagt man ja, jede Klasse hat genau eine Aufgabe. Und die ist es nicht, nur Daten zu halten. Sie sollte möglichst wenige Interna offenlegen und alle Veränderungen an sich selbst abstrakter kapseln als mit einem setXXX. Containerklassen wie std::vector o.Ä. seien hier eine Ausnahme.

    Als Anfänger geht man oft bottom-up dran - man überlegt, was die Klasse für Attribute hat und natürlich muss man alle abfragen und setzen können (wegen "was ist, wenn man aber blubb machen will"). Oft eignet sich imho ein top-down Ansatz aber besser, man überlegt also, wie man die Klasse verwenden würde. Eventuell verwendet man die noch-nicht-existente-oder-designte Klasse in Pseudocode, um ein Gefühl an die Anforderungen zu entwickeln. Und meiner Erfahrung nach kommt dann sowas Unterschiedliches dabei raus:

    Szenario: Uhr-Klasse, z.B. für ein Gadget wie hier.
    Was ich bei Anfängern oft sehe:

    class clock {
        public:
            clock();
    
            void draw( ... );
    
            int getHours() const;
            void setHours( int Hours );
            int getMinutes() const;
            void setMinutes( int Minutes );
            int getSeconds() const;
            void setSeconds( int Seconds );
    };
    

    Eigentlich aber braucht man nur sowas:

    class clock {
        public:
            clock( int Hours, int Minutes, int Seconds );
            void tick(); // Erhöht Zeit um eine Sekunde
            void draw( ... );
    };
    

    Oder vielleicht auch nur:

    class autoclock {
        public:
            autoclock();
            void draw( ... );  //Zeichnet einfach aktuelle Systemzeit
    };
    

    Das Schlechte an der allerersten Variante ist, dass das Fenster, das die Uhr beinhaltet, in seiner Funktion zum Zeichen der Uhr erst die Uhrzeit setzen muss. Klar, sind nur ein oder zwei Zeilen aber die Logik gehört da nicht hin (in diesem Anwendungsfall). Es ist zwar maximal flexibel, aber lagert die Verantwortung nur aus, weg ist sie ja nicht; nur sind Klassen eben dazu da, uns Arbeit abzunehmen.

    Abschließend: Man neigt leider (ich ja auch) oft dazu, alles so generell und flexibel wie möglich entwerfen zu wollen. So, dass auch der letzte Hansel es für ein komplett anderes Projekt verwenden könnte und ich genau diese Klasse selbst in 10 Jahren auf einem anderen Rechnertyp mit anderem OS wiederverwenden kann. Das hat aber nun mal seinen Preis, besonders im Hinblick auf die Verständlichkeit. Oft reicht es, für den aktuellen Stand zu designen und dabei ein wenig mögliche zukünftige Änderungen und Anforderungen einzuplanen.


Anmelden zum Antworten