Macro um std::map zu initialisieren



  • Weil es extrem viel Tipparbeit und redudante Informationen einsparen würde. Jede Klasse des Frameworks hat ein Type Objekt welches aus einem dutzend Macros zusammengebaut wird. Das ist im Moment keine schöne Lösung aber funktioniert halbwegs. Z. B. beinhaltet das Type Objekt Informationen über Klassennamen, Namspace, Uri, Klassenattribute, Basis-Klasse, Listen von Funktionen, Membern, Interfaces usw. Jedes Element dieser Listen besteht wiederum aus mehreren Informationen. Z.B. die Funktionsliste aus Name der Funktion, Funktionspointer, Visibility. U.a. mit besagtem Macro könnte ich das enorm vereinfachen bzw. das Type Objekt will ich nun ganz anders aufziehen sodass viele Infos die bislang manuell gepflegt werden sich automatisch ergeben. Dafür brauche ich aber besagtes Macro. Außerdem würde die Liste dann einmalig initialisiert und nicht wie jetzt jedes Element einzeln hinzugegügt.



  • @Enumerator
    Hilft dir das?

    #define REMOVE_PARENTHESES(...) __VA_ARGS__
    #define MY_MACRO(foo, entries1, bar, entries2) \
        do { \
            int f = foo; \
            int b = bar; \
            std::map<std::string, int> m1{REMOVE_PARENTHESES entries1}; \
            std::map<std::string, int> m2{REMOVE_PARENTHESES entries2}; \
            /* ... */ \
        } while (false)
    
    int main() {
        MY_MACRO(23, ({"a", 1}, {"b", 2}), 42, ({"x", 3}));
    }
    


  • @Enumerator sagte in Macro um std::map zu initialisieren:

    Weil es extrem viel Tipparbeit und redudante Informationen einsparen würde.

    Das trifft auf viele Konstrukte in C++ zu. Alle aber sind besser als Makros. Es mag sein, dass es in deinem Fall nicht anders geht, aber das ist mit aktuellem C++ eher selten der Fall.



  • @manni66 sagte in Macro um std::map zu initialisieren:

    @Enumerator sagte in Macro um std::map zu initialisieren:

    Weil es extrem viel Tipparbeit und redudante Informationen einsparen würde.

    Das trifft auf viele Konstrukte in C++ zu. Alle aber sind besser als Makros. Es mag sein, dass es in deinem Fall nicht anders geht, aber das ist mit aktuellem C++ eher selten der Fall.

    Das hier

    @Enumerator sagte in Macro um std::map zu initialisieren:

    Jede Klasse des Frameworks hat ein Type Objekt welches aus einem dutzend Macros zusammengebaut wird. [...] viele Infos die bislang manuell gepflegt werden

    ... klingt für mich aber eher nach bereits existierenden Code für einen Job. Da darf man auch schonmal auf den ersten Platz im Schönheitswettbewerb verzichten, wenn man dafür ein Projekt halbwegs sauber abschließen kann 😉

    Wenn nicht, oder wenn es das Zeitbuget erlaubt, denke ich, dass man da mit guten Konstruktoren sowie hier und da ein wenig Hilfe von std::initializer_list was schönes stricken kann, dass ohne Makros auskommt und wahrscheinlich sogar wesentlich angenehmer anzuwenden ist.

    Bei bereits "vielen manuell gepflegten Infos" will man aber velleicht nicht unbedingt eine zweite Initialisierungsmethode einführen. Da könnten Makros der Einheitlichkeit wegen die bessere Wahl sein.



  • hustbaer's Antwort ging schon in die richtige Richtung. Schön auch dass du mehrere Listen im Macro verwendest. Das wäre nämlich meine nächste Frage gewesen, ob das funktioniert aus dem "VA_ARGS" mehrere Listen zu extrahieren.

    Vielleicht nochmal etwas Pseudocode um zu zeigen was ich erreichen will:

    #define VARIABLE_LIST_ITEM(VARIABLE) \
    	{ _T(#VARIABLE), _T(#VARIABLE) }
    
    #define MY_TYPE(CLASS, ...) \
    	template <class CLASS> \
    	class MyType \
    	{ \
    	public: \
    		MyType() : \
    			m_mapVariables{ \
    				MAP_TUPLES(VARIABLE_LIST_ITEM, __VA_ARGS__) \
    			} \
    		{ \
    		} \
    	private: \
    		std::map<std::string, std::string> m_mapVariables; \
    	};
    
    class MyRect
    {
    public:
    	int m_x;
    	int m_y;
    	int m_width;
    	int m_height;
    };
    
    MY_TYPE(MyRect, (m_x, m_y, m_width, m_height))
    

    Das VARIABLE_LIST_ITEM erzeugt dabei aus der Variable zwei Strings im Beispiel. Das zweite Element in der Map würde dann aber natürlich nochmal etwas anderes sein das zusammengebaut wird. Aber um das Beispiel kurz zu halten mache ich auch mal einen String draus.

    Das Macro würde im Idealfall die Form

    MY_TYPE(MyRect, (m_x, m_y, m_width, m_height))
    

    haben. Die jetzige Lösung schaut in etwa so aus:

    MY_TYPE(
        MyRect,
        VARIABLE_LIST_BEGIN
            VARIABLE_LIST_ITEM(m_x),
            VARIABLE_LIST_ITEM(m_y)
            VARIABLE_LIST_ITEM(m_width)
           VARIABLE_LIST_ITEM(m_height)
        VARIABLE_LIST_END)
    

    was aber diverse andere unschöne Probleme zur Folge hat.

    Eigentlich will ich nur noch den Klassentyp und z.B. eine/mehrere Auflistungen von z.B. der Variablen übergeben.

    Z.B. hier habe ich etwas Maco Code gefunden (MAP_TUPLES) den ich vielversprechend finde mit dem eine Klasse zusammengebaut wird. Also Setter/Getter und Variablen. Ich will nun aber gerne halt eine bzw. mehrere maps initialisieren.

    http://coliru.stacked-crooked.com/a/358d5eb913b84a97

    Komme aber nicht drauf wie man das hinbekommt.



  • So jetzt habe ich es fast hinbekommen. Die Ausgabe ist aber noch falsch:

    'Hello World!'
    '(_x, _y, _width, _height)' | 42
    

    Warum wird das "VARIABLES" Argument vom "Init" Macro als ein einzelner Parameter erkannt anstatt als vier einzelne?

    #ifndef MAP_H_INCLUDED
    #define MAP_H_INCLUDED
    #define EVAL0(...) __VA_ARGS__
    #define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))
    #define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))
    #define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))
    #define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))
    #define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
    #define MAP_END(...)
    #define MAP_OUT
    #define MAP_GET_END() 0, MAP_END
    #define MAP_NEXT0(test, next, ...) next MAP_OUT
    #define MAP_NEXT1(test, next) MAP_NEXT0 (test, next, 0)
    #define MAP_NEXT(test, next) MAP_NEXT1 (MAP_GET_END test, next)
    #define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)
    #define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
    #define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
    #endif
    
    #define VARIABLES_LIST_ITEM(VARIABLE) \
    	{ #VARIABLE, 42 }
    
    #define VARIABLES_LIST(...) \
    	MAP(VARIABLES_LIST_ITEM, __VA_ARGS__)
    
    #define INIT(CLASS, VARIABLES, OTHER) \
    	std::map<std::string, int> list = { VARIABLES_LIST(VARIABLES) }; \
    	printf("'%s'\n", OTHER);
    
    int main()
    {
        INIT(int, (_x, _y, _width, _height), "Hello World!");
        return 0;
    };
    


  • Ok, funktioniert nun. Ich hätte natürlich das "MAP_LIST" Macro nehmen müssen...



  • @Enumerator
    Falls du das noch erweitern willst und es dir zu unübersichtlich wird: https://en.wikipedia.org/wiki/X_Macro

    Das ganze kann man schön mit include Files kombinieren die keine include-Guards haben.
    Also z.B.

    // MyBox.h:
    // Ganz normales Header-File mit Include-Guard und allem.
    // Jede Klasse definiert hier die "Parameter" für DefineClass.h und inkludiert es dann.
    
    #pragma once
    
    #include <string>
    
    namespace My {
    enum Color {};
    }
    
    #define CLASS_NAME MyBox
    #define DESCRIPTION "Blah"
    
    #define MEMBER_LIST \
        MEMBER(int, x) \
        MEMBER(int, y) \
        MEMBER(int, width) \
        MEMBER(int, height) \
        MEMBER(std::string, title) \
        MEMBER(My::Color, color)
    
    #include "DefineClass.h"
    
    // DefineClass.h
    // Dieses File wird wie ein Makro/eine Code-Generator Funktion verwendet.
    // Daher kein Include Guard hier.
    #include <iostream>
    
    #define STRINGIFY0(x) #x
    #define STRINGIFY(x) STRINGIFY0(x)
    
    class CLASS_NAME {
    public:
    #define MEMBER(MType, MName) \
        MType get_##MName() const { \
            return m_##MName; \
        } \
        void set_##MName(MType value) { \
            m_##MName= value; \
        }
    
    MEMBER_LIST // expand x-macro once
    
        std::string description() const {
            return DESCRIPTION;
        }
    
        void print() const {
            std::cout << STRINGIFY(CLASS_NAME) " : " DESCRIPTION << "\n";
    #undef MEMBER
    #define MEMBER(MType, MName) std::cout << "  " STRINGIFY(MName) " = " << m_##MName << "\n";
     MEMBER_LIST  // expand x-macro again with different definition of MEMBER
        }
    
    private:
    #undef MEMBER
    #define MEMBER(MType, MName) MType m_##MName{};
    MEMBER_LIST  // expand x-macro yet again with yet another definition of MEMBER
    };
    
    #undef MEMBER
    #undef STRINGIFY0
    #undef STRINGIFY
    
    #undef CLASS_NAME 
    #undef DESCRIPTION
    #undef MEMBER_LIST  
    

    Ist mMn. etwas einfacher zu schreiben und wesentlich einfacher zu lesen als wenn man das selbe ohne X-Makros macht.

    ps: In der klassischen Variante würde X statt MEMBER verwendet, daher die Bezeichnung X Makro.



  • Ich habe mich doch zu früh gefreut. Alle Versuche eine Liste mit Tupeln zu initialisieren sind gescheitert. Hat niemand eine Idee wie man diese doch so triviale Aufgabe mit einem Macro lösen kann. Kann doch nicht so schwer sein. Eine Schande dass der Preprocessor nach all den Jahrzehnten immer noch so ein verkrüppelter Schrott ist.

    Gibt es für unten stehendes Problem keine Lösung?

    #define INIT_MAP(...) \
    const std::map& InitMap() \
    { \
        static const std::map<std::string, int> s_map = { ??? __VA_ARGS__  ??? }; \
        return s_map; \
    } 
    INIT_MAP(("Eins", 1), ("Zwei", 2),...);
    

    Als C++ Entwickler muss ich leider immer wieder konstatieren das C++ ein Werk von Dilettanten ist. Was machen die im C++ Standardisierungskommittee eigentlich den ganzen Tag? Und wann kommt endlich der C++20 Standard? Noch in diesem Jahrzehnt? Sorry aber man muss seinem Frust auch mal Luft lassen. Bei C++ leider sehr häufig. Den ganzen Macroscheiß könnte man sich auch sparen aber C++ hat im Jahr 2021 ja immer noch keine Reflektion. Man man man.



  • @Enumerator
    Wow. Meine Motivation dir zu helfen ist gerade auf nahe Null gesunken.



  • @Enumerator Kann doch nicht so schwer sein. Eine Schande dass der Preprocessor nach all den Jahrzehnten immer noch so ein verkrüppelter Schrott ist.

    Dann bau doch einen eigenen.



  • @Tyrdal sagte in Macro um std::map zu initialisieren:

    Dann bau doch einen eigenen.

    Ziehe ich tatsächlich schon länger in Erwägung. Qt hat nicht ohne Grund den selben Weg gewählt. Hätte mir viel Zeit und Nerven gespart wenn ich das schon vor Jahren gemacht hätte. Seit Jahren quäle ich mich damit herum um die Unzulänglichkeiten von C++ herumzubauen. Jetzt wieder 5 Tage mit diesem kruden Preprozessor vergeudet und stehe bei Null. Das macht einfach keinen Spaß und ist unendlich deprimierend wenn die Sprache der limitierende Faktor ist.

    @hustbaer sagte in Macro um std::map zu initialisieren:

    @Enumerator
    Wow. Meine Motivation dir zu helfen ist gerade auf nahe Null gesunken.

    Wenn du für besagtes Problem eine Lösung hast wäre ich dir sehr verbunden wenn du diese kundtun würdest. Aber ich glaube langsam dass das einfach nicht funktioniert wie ich es mir vorstelle. Und ich wollte aus so einer trivialen Anforderung jetzt keine monatelange Forschungsarbeit machen. Wie gesagt 5 Tage habe ich schon vergeudet. Im Zweifel nehme ich jetzt erstmal die Macros die ich die Tage gebaut habe die die VA_ARGS entsprechend der Anzahl der Argumente auf N Helfer Macros anwenden. Dann muss ich mir halt 200 dieser Helfermacros schreiben um bis zu 200 Elemente in der Map abbilden zu können. Mehr Elemente bräuchte ich auch erstmal nicht. Vielleicht gibt es ja dann mit C++32 einen Weg das eleganter zu lösen.



  • #define MAP_TUPLES0(f, x, peek, ...) f x MAP_LIST_NEXT(peek, MAP_TUPLES1)(f, peek, __VA_ARGS__)
    #define MAP_TUPLES1(f, x, peek, ...) f x MAP_LIST_NEXT(peek, MAP_TUPLES0)(f, peek, __VA_ARGS__)
    #define MAP_TUPLES(f, ...) EVAL(MAP_TUPLES1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
    
    

    Wen es interessiert. Mit dem MAP_TUPLES Macro ging es doch. Ich habe mich nur die ganze Zeit durch die "unordered_map" die ich zum Testen verwendet hatte veräppeln lassen welche die Elemente absteigend "sortiert" hatte beim drüber iterieren. Ich bin dann fälschlicherweise davon ausgegangen, dass Reihenfolge der Elemente durch die Macro Orgie verdreht worden wäre was für meinen Anwendungsfall nicht akzeptabel gewesen wäre. In einem vector hat die Reighenfolge dann aber gestimmt. Muss wohl nach den 5 Tagen betriebsblind gewesen sein :).



  • @Enumerator Betriebsblind allerdings. Wer eine bestimmte Ordnung in einer unordered_map erwartet darf natürlich auf C++ schimpfen.



  • @Enumerator sagte in Macro um std::map zu initialisieren:

    Wenn du für besagtes Problem eine Lösung hast wäre ich dir sehr verbunden wenn du diese kundtun würdest.

    Ich hab dir zwei Varianten gezeigt. Wie viele willst du denn noch?


Anmelden zum Antworten