Kompilierung Taschenrechner Punkt vor Strich + Konstruktor



  • Hallo,
    ich arbeite mit dem Buch "Einführung in die Programmierung mit C++"
    von Bjarne Stroustrup. Es geht um ein Beispielprogramm "Taschenrechner mit
    Punkt vor Strich".

    Ich habe den Code, in dem der eigene Parser und das Erstellen eigener Datentypen (Tokens)
    behandelt wird größtenteils verstanden, jedoch
    1. bekomme ich es nicht hin ihn zu kompilieren
    2. habe ich den Konstruktor (

    class Token {...}
    

    ) in einem Testprogramm ausprobiert, was nicht funktioniert hat; soll ich den Konstruktor wie hier https://de.wikibooks.org/wiki/C%2B%2B-Programmierung/_Eigene_Datentypen_definieren/_Erstellen_und_Zerst%C3%B6ren machen?

    //
    // Dieser Beispielcode stammt aus Kapitel 6.9 "Programmstruktur" des Buches
    // "Einführung in die Programmierung mit C++" von Bjarne Stroustrup
    //
    
    #include "std_lib_facilities.h"
    
    //------------------------------------------------------------------------------
    
    class Token {
    public:
        char kind;        // welche Kategorie von Token
        double value;     // für Zahlen: ein Wert 
        Token(char ch)    // erstelle ein Token aus einem char
            :kind(ch), value(0) { }    
        Token(char ch, double val)     // erstelle ein Token aus einem char und einem double
            :kind(ch), value(val) { }
    };
    
    //------------------------------------------------------------------------------
    
    class Token_stream {
    public:
       Token_stream();        // erstelle einen Token_stream, der aus cin liest 
       Token get();           // lies ein Token ein (get() ist anderswo definiert)
       void putback(Token t); // lege ein Token zurück 
    private:
       bool full;    // befindet sich ein Token im Puffer?
       Token buffer; // hier legen wir ein Token ab, das mit putback() 
                     // zurückgestellt wurde
    };
    
    //------------------------------------------------------------------------------
    
    // Der Konstruktor setzt full auf false, um anzuzeigen, dass der Puffer leer ist:
    Token_stream::Token_stream()
    :full(false), buffer(0)    // kein Token im Puffer
    {
    }
    
    //------------------------------------------------------------------------------
    
    // Die Memberfunktion putback() stellt ihr Argument zurück in den Puffer von Token_stream:
    void Token_stream::putback(Token t)
    {
        if (full) error("putback(): Zurueckstellen nicht moeglich, Puffer voll");
        buffer = t;       // kopiere t in den Puffer
        full = true;      // Puffer ist jetzt voll
    }
    
    //------------------------------------------------------------------------------
    
    Token Token_stream::get()
    {
        if (full) {       // gibt es bereits ein fertiges Token?
            // Token aus dem Puffer entfernen
            full=false;
            return buffer;
        } 
    
        char ch;
        cin >> ch;    // beachten Sie, dass >> Whitespace-Zeichen wie 
                      // Leerzeichen, Zeilenumbruch, Tabulatorzeichen, etc. überspringt
    
        switch (ch) {
        case ';':    // für "Ausgeben"
        case 'q':    // für "Verlassen"
        case '(': case ')': case '+': case '-': case '*': case '/': 
            return Token(ch);        // jedes Zeichen repräsentiert sich selbst 
        case '.':
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
            {    
                cin.putback(ch);         // lege die Ziffer zurück in den Eingabestream 
                double val;
                cin >> val;              // lies eine Gleitkommazahl 
                return Token('8',val);   // '8' repräsentiert "eine Zahl" 
            }
        default:
            error("Ungueltiges Token");
        }
    }
    
    //------------------------------------------------------------------------------
    
    Token_stream ts;        // stellt get() und putback() zur Verfügung 
    
    //------------------------------------------------------------------------------
    
    double expression();    // Deklaration, damit primary() expression() aufrufen kann
    
    //------------------------------------------------------------------------------
    
    // behandelt Zahlen und Klammern 
    double primary()
    {
        Token t = ts.get();
        switch (t.kind) {
        case '(':    // behandle'(' Ausdruck ')'
            {    
                double d = expression();
                t = ts.get();
                if (t.kind != ')') error("')' erwartet");
                return d;
            }
        case '8':            // wir verwenden '8' zur Repräsentation einer Zahl
            return t.value;  // liefere den Wert der Zahl zurück
        default:
            error("Faktor erwartet");
        }
    }
    
    //------------------------------------------------------------------------------
    
    // behandelt *, / und %
    double term()
    {
        double left = primary();
        Token t = ts.get();        // lies das nächste Token aus dem Token-Stream ein
    
        while(true) {
            switch (t.kind) {
            case '*':
                left *= primary();
                t = ts.get();
                break;
            case '/':
                {    
                    double d = primary();
                    if (d == 0) error("Division durch null");
                    left /= d; 
                    t = ts.get();
                    break;
                }
            default: 
                ts.putback(t);     // stelle t wieder zurück in den Token-Stream
                return left;
            }
        }
    }
    
    //------------------------------------------------------------------------------
    
    // behandelt + und –
    double expression()
    {
        double left = term();      // liest einen Token ein und wertet ihn aus
        Token t = ts.get();        // lies das nächste Token aus dem Token-Stream ein
    
        while(true) {    
            switch(t.kind) {
            case '+':
                left += term();    // werte Term aus und addiere
                t = ts.get();
                break;
            case '-':
                left -= term();    // werte Term aus und subtrahiere 
                t = ts.get();
                break;
            default: 
                ts.putback(t);     // stelle t wieder zurück in den Token-Stream
                return left;       // keine weiteren + oder –; Antwort zurückliefern
            }
        }
    }
    
    //------------------------------------------------------------------------------
    
    int main()
    try
    {
        double val = 0;
        while (cin) {
            Token t = ts.get();
    
            if (t.kind == 'q') break; // 'q' für "verlassen"
            if (t.kind == ';')        // ';' für "jetzt ausgeben"
                cout << "=" << val << '\n';
            else
                ts.putback(t);
            val = expression();
        }
    }
    catch (exception& e) {
        cerr << "Fehler: " << e.what() << '\n'; 
        return 1;
    }
    catch (...) {
        cerr << "Hoppla: unbekannte Ausnahme!\n"; 
        return 2;
    }
    
    //------------------------------------------------------------------------------
    

    Fehlermeldungen (Visual Studio):

    Fehler LNK1120 1 nicht aufgelöste Externe ConsoleApplication2 C:\Users\test\documents\visual studio 2015\Projects\ConsoleApplication2\Debug\ConsoleApplication2.exe 1

    Fehler LNK2019 Verweis auf nicht aufgelöstes externes Symbol "_main" in Funktion ""int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)". ConsoleApplication2 C:\Users\test\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\MSVCRTD.lib(exe_main.obj) 1

    Ich habe es auch ohne die std_lib_facilities (mag ich nicht) und nur mit der <iostream> probiert, aber ohne Erfolg.

    Vielen Dank im Voraus!

    mfg
    mrcharter



  • Vermutlich hast du kein Konsolenprojekt angelegt. Dann erwartet Visual Studio WinMain statt main.



  • Vielen Dank für die Antwort,
    aber daran liegt es nicht, es ist ein Konsolenprojekt (Visual Studio 2015). Es liegt auch nicht an Visual Studio, habe es auch in Codeblocks unter Linux ausprobiert.

    MfG
    mrcharter



  • Du musst den Rumpf deiner main-Funktion in geschweifte Klammern setzen.



  • Danke,
    jedoch kommen immernoch die beiden LINK Fehler.
    MfG
    mrcharter



  • Welchen Code übersetzt du?



  • Naja den Code oben, die Klammern der main-Funktion habe ich natürlich berichtigt.

    MfG
    mrcharter



  • Der funktioniert wunderbar, sogar ohne die zusätzlichen Klammern.



  • Woher hast du den Header? Der funktioniert:

    http://stroustrup.com/Programming/PPP2code/std_lib_facilities.h



  • MFK schrieb:

    Du musst den Rumpf deiner main-Funktion in geschweifte Klammern setzen.

    weiß nicht genau, was der standard hierzu sagt, aber msvc (2015) zumindest compiliert

    int main()
    try{}
    catch(...){}
    

    anstandslos
    hatte ich auch als erstes probiert als ich den thread gefunden habe und war verwundert



  • manni66 schrieb:

    Woher hast du den Header? Der funktioniert:

    http://stroustrup.com/Programming/PPP2code/std_lib_facilities.h

    Ich benutze den Header. Kann das an Visual Studio liegen (Ich habe auch das Problem, dass Visual Studio abstürzt, wenn ich ein neues Projekt erstelle)?

    Und was genau ist jetzt der Unterschied zwischen dem Konstruktor von dem Taschenrechnerprogramm

    Token {...}
    

    und soeinem Konstruktor https://de.wikibooks.org/wiki/C%2B%2B-Programmierung/_Eigene_Datentypen_definieren/_Erstellen_und_Zerstören ?

    MfG
    mrcharter



  • mrcharter schrieb:

    Kann das an Visual Studio liegen (Ich habe auch das Problem, dass Visual Studio abstürzt, wenn ich ein neues Projekt erstelle)?

    Installier bitte VS richtig, bzw. beheb die offfensichtlichen Probleme mit Deiner Installation.

    unskilled schrieb:

    weiß nicht genau, was der standard hierzu sagt, aber msvc (2015) zumindest compiliert

    int main()
    try{}
    catch(...){}
    

    anstandslos

    Siehe hier: http://en.cppreference.com/w/cpp/language/function-try-block



  • mrcharter schrieb:

    Und was genau ist jetzt der Unterschied zwischen dem Konstruktor von [code="cpp"]Token {...}[code="cli"] und soeinem Konstruktor https://de.wikibooks.org/wiki/C%2B%2B-Programmierung/_Eigene_Datentypen_definieren/_Erstellen_und_Zerstören machen?

    Keine Ahnung. Deine Frage ist völlig kaputt. Es gibt einen Vorschauknopf, da kann man sich alles ansehen, bevor man es abschickt.



  • manni66 schrieb:

    Keine Ahnung. Deine Frage ist völlig kaputt. Es gibt einen Vorschauknopf, da kann man sich alles ansehen, bevor man es abschickt.

    Sorry, war ein Versehen, habe es berichtigt.

    Und was genau ist jetzt der Unterschied zwischen dem Konstruktor von dem Taschenrechnerprogramm
    C++:
    Token {...}
    und soeinem Konstruktor https://de.wikibooks.org/ ....... tellen_und_Zerst%C3%B6ren ?

    Hat darauf jemand eine Antwort?

    Ich bin jetzt dabei Visual Studio zu reparieren, und habe den Code unter Codeblocks ausprobiert.
    Ich habe folgende Fehlermeldungen bekommen:

    ||=== Build: Debug in trtest (compiler: GNU GCC Compiler) ===|
    /usr/include/c++/4.9/backward/backward_warning.h|32|warning: #warning This file includes at least one deprecated or antiquated header which may be removed without further notice at a future date. Please use a non-deprecated interface with equivalent functionality instead. For a listing of replacement headers and interfaces, consult the file backward_warning.h. To disable this warning use -Wno-deprecated. [-Wcpp]|
    /usr/include/c++/4.9/bits/locale_facets_nonio.h|1869|error: template-id ‘do_get<>’ for ‘String std::messages<char>::do_get(std::messages_base::catalog, int, int, const String&) const’ does not match any template declaration|
    /usr/include/c++/4.9/bits/locale_facets_nonio.h|1869|note: saw 1 ‘template<>’, need 2 for specializing a member function template|
    /home/tobias/Dokumente/C++/trtest/main.cpp||In member function ‘Token Token_stream::get()’:|
    /home/tobias/Dokumente/C++/trtest/main.cpp|84|warning: control reaches end of non-void function [-Wreturn-type]|
    /home/tobias/Dokumente/C++/trtest/main.cpp||In function ‘double primary()’:|
    /home/tobias/Dokumente/C++/trtest/main.cpp|113|warning: control reaches end of non-void function [-Wreturn-type]|
    ||=== Build failed: 1 error(s), 3 warning(s) (0 minute(s), 2 second(s)) ===|
    

    Kann mir jemand sagen, was ich falsch mache?

    Vielen Dank im Voraus!

    MfG

    mrcharter



  • /usr/include/c++/4.9/backward/backward_warning.h|32|warning: #warning This file includes at least one deprecated or antiquated header which may be removed without further notice at a future date. Please use a non-deprecated interface with equivalent functionality instead. For a listing of replacement headers and interfaces, consult the file backward_warning.h. To disable this warning use -Wno-deprecated. [-Wcpp]|
    

    steht doch alles drin. welcher header das ist, weiß ich aber nicht. evtl strstream oder so was includiert?

    /usr/include/c++/4.9/bits/locale_facets_nonio.h|1869|error: template-id ‘do_get<>’ for ‘String std::messages<char>::do_get(std::messages_base::catalog, int, int, const String&) const’ does not match any template declaration| 
    /usr/include/c++/4.9/bits/locale_facets_nonio.h|1869|note: saw 1 ‘template<>’, need 2 for specializing a member function template|
    

    vergleich doch mal aufruf und derklaration der fkt.

    /home/tobias/Dokumente/C++/trtest/main.cpp||In member function ‘Token Token_stream::get()’:| 
    /home/tobias/Dokumente/C++/trtest/main.cpp|84|warning: control reaches end of non-void function [-Wreturn-type]|
    

    es kann passieren, dass deine fkt nichts zurückgibt, obwohl sie das muss.
    das solltest du selbst erkennen.

    /home/tobias/Dokumente/C++/trtest/main.cpp||In function ‘double primary()’:| 
    /home/tobias/Dokumente/C++/trtest/main.cpp|113|warning: control reaches end of non-void function [-Wreturn-type]|
    

    noch mal.

    bb

    PS: wenn du so weiter machst, kann es ganz schnell passieren, dass du gar keine antworten mehr bekommst, weil du etwas wenig eigeninitiative zeigst.



  • mrcharter schrieb:

    Und was genau ist jetzt der Unterschied zwischen dem Konstruktor von dem Taschenrechnerprogramm
    C++:
    Token {...}
    und soeinem Konstruktor https://de.wikibooks.org/ ....... tellen_und_Zerst%C3%B6ren ?

    Hat darauf jemand eine Antwort?

    Ich sehe keinen Unterschied. Was meinst du?

    mrcharter schrieb:

    und habe den Code unter Codeblocks ausprobiert.
    Ich habe folgende Fehlermeldungen bekommen:

    Mit einem gcc 4.9? Keine Ahnung ob es damit klappt. Mit gcc 6.3 auf Linux geht es mit dem gezeigten Code und der von mir verlinkten std_lib_facilities.h.



  • Vielen Dank für die Antworten.

    Code:
    /usr/include/c++/4.9/bits/locale_facets_nonio.h|1869|error: template-id ‘do_get<>’ for ‘String std::messages<char>::do_get(std::messages_base::catalog, int, int, const String&) const’ does not match any template declaration|
    /usr/include/c++/4.9/bits/locale_facets_nonio.h|1869|note: saw 1 ‘template<>’, need 2 for specializing a member function template|

    vergleich doch mal aufruf und derklaration der fkt.

    Habe jetzt alle bis auf die beiden berichtigt. Leider habe ich mich nicht mit Templates nicht beschäftigt und kann darum nichts damit anfangen (bzw. habe es mir angeschaut und bin leider zu keinem Ergebnis gekommen). Es wäre nett, wenn mir jemand sagen kann, was ich noch ändern muss damit es passt.

    Ich sehe keinen Unterschied. Was meinst du?

    Hat sich erledigt.

    MfG

    mrcharter



  • mrcharter schrieb:

    was ich noch ändern muss damit es passt

    Nichts. Installiere einen aktuellen Compiler (für Windows VS2017).


Anmelden zum Antworten