Macro um std::map zu initialisieren



  • 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?


Log in to reply