SolidFramework



  • Hey zusammen,

    ich bastel zur Zeit an einem C++-Framework, welches als Basis für eine Game Engine dienen soll.
    Da ich aber Teile des Frameworks auch in anderen Projekten wiederverwenden will, ist das ganze in sinnvolle Subprojekte aufgeteilt.
    Im Moment habe ich eine lauffähige Version eines Testing-Frameworks, eines einfachen Loggers und einer relativ spezifischen Markup-Sprache für UI und Level.

    Da ich selber die API für ziemlich gelungen halte, aber das Projekt nicht groß veröffentlichen möchte, bevor ich nicht von ein paar weiteren Leuten ein "sieht eigentlich ganz gut aus" bekommen habe, dachte ich mir, ich frag mal rum, ob wer Interesse/Zeit/Lust/... hätte, sich mal den Code anzuschauen, Feedback zu geben und mit der Library mal ein bisschen rumzuspielen.

    Um einene kleinen Vorgeschmack zu geben, was auf einen zukommt:
    Hier mal ein kleines Beispiel, wie ein DOM eines Markup-Dokumentes erstellt wird:

    # Solid Markup 
    Root {
        Container(size=20) {
            c1:Child(a = [val]);
            c2:Child(a = [val]);
        }
        data : Parser {
            Option(key=stopOnError, value=true);
            Option(key=createFormattedDocument, value=false);
        }
    }
    
    #include <solid/markup.hpp>
    #include <solid/log.hpp>
    
    using namespace std;
    using namespace solid::log;
    using namespace solid::markup;
    
    void analyze()
    {
        unique_ptr<Document> doc = Markup().load("example.sll");
    
        Message() << "Root node: " << doc->root().className();
    
        // Using named nodes
        Message() << "Parser Options:";
    
        Node &parser = doc->namedNode("data");
        for(size_t i = 0; i < parser.childCount(); i++) {
            Node &child = parser.childAt(i);
            Message() << child.attribute("key").value() << "=" << child.attribute("value").value();
        }
    
        // Using attribute bindings
        Node &c1 = doc->namedNode("c1");
        Node &c2 = doc->namedNode("c2");
        c1.attribute("a").setValue("hello bindings");
        Message() << "a=" << c2.attribute("a").value();
    
    }
    

    Ich hoffe das hat euch nen kleinen Vorgeschmack gegeben, ich würde mich freuen, wenn es ein/zwei Interessenten gibt!

    Grüße
    Felix



  • Ich hab' ehrlich gesagt keinen Plan was das darstellen soll.
    Generische Datenformate wie XML oder JSON gibt es schon einige, wieso also 'was eigenes erfinden?
    Die Frage ist durchaus ernst gemeint. Erklär mal wie sich deine "Markup Sprache" von XML und JSON unterscheidet, und wozu diese Unterschiede gut sind.

    An einem non-Standard Format wird nämlich nicht so schnell irgendwer Interesse haben, wenn er dadurch nicht deutliche Vorteile hat. Sonst überwiegen nämlich immer die Nachteile wie z.B. dass es keinerlei Tool-Support dafür gibt. (Was bei Syntax-Highlighting anfängt und dann weiter geht mit Sachen wie XSLT Prozessoren oder Schema-Validation.)



  • hustbaer schrieb:

    Generische Datenformate wie XML oder JSON gibt es schon einige, wieso also 'was eigenes erfinden?
    Die Frage ist durchaus ernst gemeint. Erklär mal wie sich deine "Markup Sprache" von XML und JSON unterscheidet, und wozu diese Unterschiede gut sind.

    Nun, wie gesagt, Ziel des Projektes ist es, eine Library zu schaffen, auf der ich eine Game Engine aufbauen kann. Es existieren relativ viele Prototypen, welche auch alle sehr gut funktionieren. Nur als ich versucht habe, eine GUI-Layouting-Engine zu schreiben, kam der Punkt, an dem ich mir überlegen musste: JSON oder XML.
    Anforderungen waren:

    • schnell Tipp/Lesbar
    • einfach zu lernen
    • Klare Strukturen
    • einfacher Support für "Bindings" (bekannt aus XAML/WPF)

    Ich persönlich bin generell kein Freund von JSON, also erst mal XML angeschaut:
    **Schnell Tippbar:**Naja, ich muss jeden Tag doppelt tippen, also nein (und ja, es gibt editoren, die das machen)
    **Schnell Lesbar:**Ja, definitiv. Durch die <> stechen Tags sofort raus und man kann gut navigieren
    **Einfach zu lernen:**Jeder sollte XML können, also gegeben
    Klare Strukturen: Was mich schon an XAML gestört hat und sonst auch oft praktiziert wird: Attribute und Kindelemente fröhlich mischen. Einmal wird der name als Attribut an einem Element angebracht, bei anderen Formaten als Element. Also nicht unbedingt ganz so klare Strukturen, der Gedanke war also, alle Kind-Objekte als Kindelement, alle (Objekt)Attribute als XML-Attribute
    **Support für Bindings:**Nun, ein Binding ist im Endeffekt eine Variable, die an ein Attribute gebunden wird. Bei XML hab ich jetzt zwei Möglichkeiten:
    1. Attribute werden mit einer speziellen Syntax wie in XAML gebunden. Problem: Man muss wieder eine "zweite" Sprache lernen, welche die Bindings ausdrückt.
    2. Attribute werden über Kind-Elemente mit einem Binding-Element gebunden. Man bleibt in XML, verletzt aber die klare Struktur.

    So, jetzt zu JSON:
    **Schnell Tippbare:[b]In meinen Augen nein, außer ich mappe auf meiner Tastatur Anführungszeichen und Leerzeichen auf die jeweils andere Taste 😛
    [b]Schnell Lesbar:**Ich halte XML für lesbarer als JSON, da JSON den Unterschied von Objekt und Array kennt und das ganze "nur" durch einen Unterschied in der Klammerung ausdrückt. Also ohne Syntaxhighlighting schon echt schwer zu erkennen.
    **Einfach zu lernen:**Wie bei XML, nur halte ich die Regelung mit den Anführungszeichen für Identifier echt für grauenhaft
    **Klare Strukturen:**Aufzählungen via Array, Attribute via Objekt-Attrib, Check. Trifft die Anforderungen ganz gut.
    **Support für Bindings:**Ähnliche Problematik wie bei XML, entweder ich nehme
    1. Spezielle Syntax als String-Attributwert
    oder
    2. ich ersetze einen attributwert durch ein objekt.
    Problematik bei 1 ist die selbe wie bei XML, zu 2tens: Wenn meine Sprache es erlaubt, werde ich auch die Möglichkeit beiten, verschiedene Datentypen an ein Attribut zu binden. Da aber dann ein Binding von einem Binding-Objekt nicht mehr unterscheidbar ist, habe ich hier wieder ein Problem.

    **tl;dr:**Weder XML noch JSON entsprechen meinen Anforderungen

    Zu der zweiten Frage noch eine Sache: Meine Sprache kann ausschließlich Baumstrukturen darstellen, wobei ein Knoten nicht mehr als eine Gruppe an Kindknoten haben kann. Das ist so gewollt und ist kein "Designfehler" 😉

    Also hab ich mich für eine Sprache entschieden, welche schnell zu lernen ist (da Syntax sehr einfach), klare Strukturen hat (Attribute immer in (), Kinder immer in { }). Zudem supported die Sprache das Konzept "Binding" von Haus aus, die dazugehörige Parser-Library bietet ebenfalls eine vollständig funktionsfähige Implementierung dazu.

    (Was bei Syntax-Highlighting anfängt und dann weiter geht mit Sachen wie XSLT Prozessoren oder Schema-Validation.)

    Für Syntax Highlighting bau ich nebenher ein Kate-XML-File, welches die Sprache highlighted, zum Thema Validation:
    Die Library wird Funktionen zur Validierung von Dokumenten bieten, welche man auch sicher ohne weiteres in ein passendes formalisiertes Tool packen kann, welche eine Datei validiert (Danke für den Gedanken btw!).

    Ich hoffe ich konnte meinen Standpunkt für den Bau der Markup-Library ein bisschen klarer machen

    Grüße
    Felix



  • MasterQ32 schrieb:

    Ich hoffe ich konnte meinen Standpunkt für den Bau der Markup-Library ein bisschen klarer machen

    Klarer vielleicht, aber wirklich überzeugt bin ich auch nicht. Ich glaube aber, dass viele durchaus schnell dabei sind, eine eigene (Markup) Sprache zu schreiben, und die dann auch schön finden, andere davon aber viel schwieriger zu überzeugen sind. Ich seh deine Sprache zum ersten mal und denk mir erstmal, hmm... Wäre ich an deiner Stelle gewesen, hätte ich mir vielleicht aber auch gedacht, warum nicht, schreib ich halt was, das so aussieht, wie ich mir das vorstelle.



  • Nun klar, jede neue Sprache hat ohne Erklärung erst mal begrenzt Sinn 😉
    Aber um die Sprache sollte es im Thread eigentlich ja auch nicht gehen, ich hab aber nichts gegen "Offtopic" in dem Sinne, Feedback ist immer gut

    Hab heute weiter am Framework gewerkelt und eine Library auf der Markup-Library aufgesetzt: SolidLayout
    Die Library ist ein Layouting-Framework, welches mit SM beschriebene UI-Layouts erstellt und richtig positioniert. Dabei hat die Library selbst keine konkrete Implementierung von Widgets, sondern bietet nur die rohe Layouting-Logik.

    Dabei hab ich festgestellt, dass die Markup-Library tatsächlich ziemlich solide ist, aber auch noch ein paar kleine Macken hatte (Instanzierung von Document anstelle von eigener Dokumentklasse, ...)
    Das Ganze ist jetzt behoben und funktioniert hervorragend, bin also recht zufrieden.



  • Kennst du Adam und Eve von Adobe? Ist nicht uninteressant:

    http://stlab.adobe.com/group__asl__overview.html

    Das eine ist eine Layout Sprache (schaut auch so ähnlich aus wie deine), die ebenfalls von konkreten Widgetimplementierungen unabhängig ist. Das andere beschreibt das Model hinter dem UI, also wie die ganzen Elemente zusammenhängen, was man wo einstellen kann, und wie sich die anderen Werte dann ändern. Ich wollte mir das alles mal noch genauer anschauen, bin aber (noch) nicht dazu gekommen.



  • Coole Sache, danke für den Link, werd mir das mal in Ruhe zu Gemüte führen.



  • MasterQ32 schrieb:

    **tl;dr:**Weder XML noch JSON entsprechen meinen Anforderungen

    Natürlich nicht.
    Man kann Anforderungen immer so definieren bzw. Evaluierungen immer so durchführen dass nix mehr übrig bleibt.



  • hustbaer schrieb:

    Natürlich nicht.
    Man kann Anforderungen immer so definieren bzw. Evaluierungen immer so durchführen dass nix mehr übrig bleibt.

    Vertraue keiner Statistik, die du nicht selbst gefälscht hast, klar 😛

    Aber nein, es geht bei meinem Framework ja zum einen darum, für mich eine solide Basis zu basteln, welche aber nicht "überladen" ist, wie zum Beispiel Qt.
    Zum anderen aber auch als Lernplattform, in der ich meine C++-Skills weiter steigern kann, ich sehe viele Libraries in C++, die einfach keine "schöne" (ich weiß, Definitionssache) API hat, welche leicht zu bedienen und verstehen, aber auch gleichzeitig nicht einschränkend ist. Zudem gibt es immer ein paar Sachen, die man mal gemacht haben muss...



  • OK.
    Also möchtest du die Frage ob es Sinn macht eine eigene Sprache zu definieren aussen vor lassen. Soll mir recht sein 🙂

    Wenn du Feedback möchtest, dann wäre denke ich ein grösseres Beispiel hilfreich.
    Vielleicht was konkretes, also etwas wo man sieht wie die Definition z.B. eines Dialogs mit ein paar Controls drinnen aussehen würde.



  • Da ich mittlerweile einen Großteil der Layout Engine fertig habe, kann ich ja mal ein Beispiel mit selbiger bringen:

    Nutzung der Layout-Engine als DOM mit SDL2:

    #include <map>
    #include <stdio.h>
    #include <SDL2/SDL.h>
    #include <SDL2/SDL_image.h>
    #include <solid/layout/widget.hpp>
    #include <solid/layout/layoutmarkup.hpp>
    
    using namespace solid;
    using namespace solid::markup;
    using namespace solid::layout;
    
    SDL_Window *window;
    SDL_Renderer *renderer;
    
    // Texture caching and loading
    std::map<std::string, std::shared_ptr<SDL_Texture>> cache;
    std::shared_ptr<SDL_Texture> texture(const std::string &fileName)
    {
        if(::cache.count(fileName) == 0) {
            SDL_Texture *tex = IMG_LoadTexture(::renderer, fileName.c_str());
            std::shared_ptr<SDL_Texture> ptr(tex, [](SDL_Texture*texture) {
               SDL_DestroyTexture(texture);
            });
            ::cache.insert({fileName,ptr});
        }
        return ::cache.at(fileName);
    }
    
    void exit_sdl(int error = 1)
    {
        printf("SDL_GetError() => '%s'\n", SDL_GetError());
        exit(error);
    }
    
    // Recursive draw routine
    void paint(Widget *widget)
    {
        if(widget == nullptr) {
            return;
        }
    
        SDL_Rect target;
        target.x = widget->position().x;
        target.y = widget->position().y;
        target.w = widget->size().x;
        target.h = widget->size().y;
    
        // Paint all widgets with images.
        if(widget->hasAttribute("image")) {
            SDL_RenderCopy(::renderer, texture(widget->attribute("image").value()).get(), nullptr, &target);
        }
    
        for(Widget *w : widget->children<Widget>())
        {
            ::paint(w);
        }
    }
    
    int main(int, char*[])
    {
        if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
            exit_sdl();
        }
        atexit(SDL_Quit);
    
        if(IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG) == 0) {
            exit_sdl();
        }
        atexit(IMG_Quit);
    
        if(SDL_CreateWindowAndRenderer(1280, 720, SDL_WINDOW_SHOWN, &::window, &::renderer) < 0) {
            exit_sdl();
        }
    
        // Register classes and load markup
        LayoutMarkup markup;
        markup.registerWidgetClass<Widget>("Widget");
        std::unique_ptr<Layout> layout = markup.load("demo.sll");
    
        while(true)
        {
            SDL_Event e;
            if(SDL_PollEvent(&e))
            {
                switch(e.type)
                {
                case SDL_QUIT:
                    goto quit;
                case SDL_KEYDOWN:
                {
                    switch(e.key.keysym.sym)
                    {
                    case SDLK_ESCAPE:
                        goto quit;
                    }
                    break;
                }
                }
            }
            // Layout UI
            {
                Size size;
                SDL_GetWindowSize(::window, &size.x, &size.y);
                layout->layout(size);
            }
            // Render UI
            {
                SDL_RenderClear(::renderer);
                ::paint(&layout->root<Widget>());
                SDL_RenderPresent(::renderer);
            }
            SDL_Delay(16);
        }
    quit:
        return 0;
    }
    
    Widget(image="fractale-06-scenery-antenna.jpg")
    {
        Widget(image="crosshair.png",verticalAlignment=center,horizontalAlignment=center,size="96;96");
        Widget(image="minimap.png",verticalAlignment=top,horizontalAlignment=right,size="200;200");
        StackLayout(size="auto;auto",verticalAlignment=bottom,horizontalAlignment=left,orientation=vertical)
        {
            Widget(image="endurance.png", size="200;64");
            Widget(image="healthbar.png", size="200;64");
        }
    }
    

    Das Ergebnis:
    http://i.imgur.com/Lc0cif9.jpg (Gibt es keine Image-Tags?)

    Mit dem Script von oben lassen sich beliebige Layouts erschaffen, wenn man jetzt noch eine eigene Widget-Klasse erstellt, welche das Image-Attribute bereitstellt, kann man sogar die Größen direkt aus dem Bild übernehmen, wenn als Wert "auto" geliefert wird.

    Grüße
    Felix


Anmelden zum Antworten