C vs C++ bei OSDEV


  • Mod

    Die Stärken moderner Programmiersprachen bestehen in der Fehlervermeidung und -überwachung. Allerdings hat man dafür bei C einen einfacheren Umgang mit der Materie. C++ fordert dem Entwickler hier deutlich mehr ab.

    Warum nicht gleich Java für OSDEV?



  • Erhard Henkes schrieb:

    Warum nicht gleich Java für OSDEV?

    Unter anderem weil Java sich wieder von der Politik des Fehlervermeidens verabschiedet, dabei wieder unter das Niveau von C fällt (,wobei ich mit "Vermeiden" nicht meine, daß man sie nur auffängt), und langsamer ist und man nur über Umwege an die Hardware kommt, was für ein BS, wie ich füchte, dann doch zu nervig wird.



  • Erhard Henkes schrieb:

    Allerdings hat man dafür bei C einen einfacheren Umgang mit der Materie. C++ fordert dem Entwickler hier deutlich mehr ab.

    Was meinst du denn konkret damit? Der meiste Code von PrettyOS ist ebenfalls valider C++ Code. An vielen Stellen fehlt nur ein expliziter Cast oder ein extern "C" wenn die Funktionen von Assembler aus aufgerufen werden. Das ist weder schwieriger noch weiter von der Materie entfernt.

    Im Gegenzug erhoffe ich mir das du nicht mehr so etwas wie an konkrete Laufwerke gekoppelte Dateisyetemfunktionen schreibst. Ich habe dir ja schon prophezeit, dass die Fehlersuche die du jetzt für das Fat Dateisystem auf Diskette durchführst, in ähnlicher Form wieder auftritt wenn du beides voneinander trennst oder die Fat Funktionen auf andere Laufwerke überträgst. Es wäre deutlich einfacher wenn man es also gleich richtig macht. Komplizierter ist das ganze sicherlich nicht.



  • C++ hat nach meiner Ansicht den Vorteil, dass man mehr Semantik im Code unterbringen kann. Und mit Templates wird das verwenden von Datenstrukturen stark vereinfacht.

    Im Kernel würde ich aber natürlich auf den Runtime-abhängigen Teil verzichten (also Exceptions, RTTI).

    @abc.w
    size_t anstelle uint32_t!

    Aber der Code wirkt teilweise ein bisschen komisch

    size = size;
    

    oder anstelle von break dieses Konstrukt

    for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
    {
    //...
          i = (MAX_MEMORY_BLOCKS - 1u);
    }
    

    Grobe Fehler:
    size wird ignoriert: wenn man mehr als MAX_BLOCK_SIZE Speicher anfordert, dann bekommt man einen Pointer auf einen zu kleinen Speicherbereich.

    Und wenn man mehr als MAX_MEMORY_BLOCKS anfordert, dann überschreibt man immer wieder den ersten Block.



  • rüdiger schrieb:

    @abc.w
    size_t anstelle uint32_t!

    Ja... Hm, habe nur uint32_t und uint8_t als Datentypen.

    rüdiger schrieb:

    Aber der Code wirkt teilweise ein bisschen komisch... ...Grobe Fehler...

    Ja, zu viel von KISS (keep it sehr stupid)... 🙂 Ist sowieso nur zum Testen und Hauptsache, kein malloc(). Funktionierenden Kernel in C++ habe ich auch nicht und bin sehr weit davon entfernt, aber es hat seinen Reiz...



  • Erhard Henkes schrieb:

    Zeige mir eine konkrete Stelle in PrettyOS im Kernel, bei der uns C++ einen greifbaren Vorteil bringen würde.

    Im Moment könnte ich es ganz gut für das Kontrollieren des Interrupt-Flags gebrauchen (sti/cli):
    Wenn ich eine Routine schützen will, sieht das ja so aus:

    void foo()
    {
        cli();
    
        if ( x )
        {
            sti();
            return;
        }
    
        sti();
    }
    

    Das ist ja noch halbwegs OK. Das Problem dabei ist nur, dass foo u.U. eigentlich gar nicht die Interrupts anschalten darf. Z.B. wenn foo aus einer ebenfalls geschützten Funktion aufgerufen wird:

    void bar()
    {
        cli();
        foo();
        // Hier sind Interrupts leider wieder eingeschaltet
        doImportantStuff();
        sti();
    }
    

    D.h. wir brauchen eigentlich auch noch Abfrage, ob Interrupts beim cli-Aufruf gesetzt waren:

    void foo()
    {
        // cli sollte zurückgeben, ob Interrupts vorher gesetzt waren.
        bool interrupts_enabled = cli();
    
        if ( x )
        {
            if ( interrupts_enabled )
                sti();
            return;
        }
    
        if ( interrupts_enabled )
            sti();
    }
    

    Das an jeden Ausgang zu setzen, ist irgendwie eklig. Mit C++ wäre das doch eher einfacher:

    void foo()
    {
        DisableInterrupts interrupt_protection;
        // ...
    }
    


  • Man müsste ja noch bei jedem return aufpassen, die Interrupts wieder einzuschalten, was man in einem Destruktor machen könnte 🤡



  • abc.w schrieb:

    Man müsste ja noch bei jedem return aufpassen, die Interrupts wieder einzuschalten, was man in einem Destruktor machen könnte 🤡

    Darauf wollte ich eigentlich hinaus :p "DisableInterrupts" als Ersatz für das Ausschalten der Interrupts, merken ob sie an waren und gegebenenfalls wieder Anschalten der Interrupts beim rausspringen. Und das mit einem Einzeiler.


  • Mod

    Das Thema C vs C++ ist nicht neu:
    http://forum.osdev.org/viewtopic.php?f=15&t=22406&start=0

    Die Diskussion ist sehr unstrukturiert, aber einige Erfahrungsbeiträge sind lesenswert. In Summe gesehen gehen beide Sprachen, wenn man es richtig anpackt.

    Ich persönlich bin überrascht, wie gut man in C einen Kernel programmieren kann.
    Daher sehe ich keinen wirklichen Vorteil in C++.



  • Daher sehe ich keinen wirklichen Vorteil in C++.

    Ich sehe durchaus Vorteile. Funktionsüberladung ist immer gut^^, Dinge wie Listen ließen sich viel leichter nutzen (Konstruktoren/Destruktoren, u.a.), statt void* data mitzuführen könnte man einfach von einer Basisklasse Erben.



  • Das wichtigste: Information Hiding! Sprich Kapselung von Daten, so das man einen kontrollierten Zugriff erlauben kann. Völlig unabhängig von Vtable, Template-Bloat u.a. Mythen die Performanceverlust bedeuten sollen.

    Gerade die Kapselung kann viel zur Qualität eines Projektes (egal ob Kernel oder Anwendungen) beitragen. Sowohl weniger Fehler zur Laufzeit, als auch einer einfachen Wartung.


  • Mod

    Kapselung ( engl.: information hiding oder encapsulation ) ist in der Tat der wesentliche Effekt der Objektorientierung. Das ist ein klarer Pluspunkt für C++.
    http://www.henkessoft.de/C++/C++/kapselung.htm



  • Information Hiding kann man in C auch betreiben, aber Encapsulation nicht. Zwei Begriffe die nicht äquivalent sind.



  • Erhard Henkes schrieb:

    .. Ich persönlich bin überrascht, wie gut man in C einen Kernel programmieren kann. ...

    Liegt es vielleicht daran, dass die Tools gcc und binutils verwendet werden? 😉


  • Mod

    Dass man auch in C++ interessante OS bauen kann, wird hier von James Molloy und Jörg Pfähler ("bluecode") gezeigt:
    http://www.lowlevel.eu/wiki/Pedigree



  • Imo gibt es nur einen einzigen Grund heutzutage noch C zu benutzen: Wenn es für eine Zielplattform keinen C++ Compiler gibt. Alleine schon wegen templates, overloading und RAII würde ich mich jederzeit für C++ entscheiden wenn ich die Wahl hätte. Dass C++ einen inhärenten Performancenachteil gegenüber C hätte ist ein Märchen, man kann genausogut Beispiele aufzeigen wo das Gegenteil der Fall ist (z.B. das allseits beliebte std::sort vs qsort()). Natürlich hat C++ einige Sprachfeatures die einen gewissen Overhead mit sich bringen. Allerdings darf man dabei nicht vergessen dass, wenn man ähnliche Systeme direkt in C implementieren würde, am Ende C Code rauskommt der mehr oder weniger genau das tut was der C++ Compiler einem abnehmen würde und daher auch vergleichbaren Overhead mit sich bringt (Beispiel die gern verteufelten virtuelle Funktionen: In C greift man dafür eben in irgendeiner Form auf Function Pointer zurück und hat am Ende den selben Overhead).


  • Mod

    Information Hiding kann man in C auch betreiben

    Kannst du dies bitte an Beispielen darstellen?



  • foo.h:

    struct foo;
    
    struct foo* create_foo(void);
    void do_it(struct foo* xyz);
    

    foo.c:

    #include "foo.h"
    
    struct foo {
        int bar;
        char baz[42];
    };
    
    struct foo* create_foo(void) { return calloc(1, sizeof(struct foo)); }
    void do_it(struct foo* xyz) { ... }
    

    Von außen sieht niemand, wie eine struct foo tatsächlich aussieht, man hantiert nur mit Pointern darauf.



  • Stellen wir fest:
    1. Es ist sehr nützlich Kapselung/Information-Hiding (ich meine nicht Implementation-Hiding ähnlich Pimpl) zu betreiben.
    2. Es ist mit C-Mitteln in gewisser Weise möglich.

    Meine Frage: ist es in der C-Welt Usus, es so zu machen? Falls nicht, bitte ich um eine Erklärung, warum es nicht Usus ist. 😃



  • @taljeth
    Danke, genau sowas hatte ich Sinn.

    Man sieht aber auch es gibt Probleme, wenn man die Idee von vorher aufgreift, Polymorphie durch Funktionszeiger zu emulieren.


Anmelden zum Antworten