In Datei schreiben



  • Hi, ich habe ein Problem beim schreiben von Benutzereingaben in eine Datei.
    Hier erstmal das \1:

    void schreibeDatei(string dateiname){
    	int anzahl;
    	string daten;
    
    	ofstream datei;
    	datei.open(dateiname.c_str(), ios::app);
    	if(datei.is_open()) {
    		cout << "Wie viele Datensaetze sollen eingetragen werden? " << endl;
    		cin >> anzahl;
    		cin.clear();
    		for(int i = 0; i < anzahl; i++) {
    			cout << i+1 << ". Eintrag:" << endl;
    			getline(cin, daten);
    			datei << daten << endl;
    		}
    	} else
    		cout << "Datei konnte nicht geoeffnet werden!" << endl;
    	datei.close();
    }
    

    Das Problem dabei ist, das der erste Eintrag immer ein Zeilenumbruch ist. Heißt, die Schleife begibt schon beim 2. Eintrag. Der erste wird automatisch befüllt. Ich habe allerdings keine Ahnung warum das so ist 😕
    Vielleicht kann mir ja jemand helfen.
    LG


  • Mod

    Was macht cin.clear? Ich kann dir jetzt schon verraten, dass es nicht das tut, was du anscheinend denkst, aber ich überlasse es mal dir zur Übung, herauszufinden, was es wirklich tut.

    Man könnte nun zwar etwas konstruieren, was dem nahe kommst, was du derzeit von dem clear erwartest (irgendwas mit ignore, wenn du interessiert bist) aber ein besserer Vorschlag: Wieso überhaupt die Anzahl der Datensätze im Voraus festlegen? Lass den Benutzer doch einfach so lange Einträge vornehmen, bis ein Abbruchkriterium erfüllt ist. Beispielsweise Eingabe eines leeren Datensatzes und/oder wenn der Benutzer die Eingabe für beendet erklärt (CTRL+Z in Windows, CTRL+D in anderen Systemen).
    Ungetestet:

    void schreibeDatei(string dateiname){
        ofstream datei(dateiname.c_str(), ios::app);
        if(datei) {
            for(int i = 1; true; i++) {
                cout << i << ". Eintrag:\n";
                string daten;
                if (getline(cin, daten) && daten.size())
                    datei << daten << '\n';
                else
                    break;
            }
        } else
            cout << "Datei konnte nicht geoeffnet werden!\n";
    }
    

    Derzeit gefällt mir noch nicht, was die Funktion hier überhaupt tut. schreibeDatei impliziert einen viel kleineren Funktionsumfang, als er hier gegeben ist. Deine Funktion ist aber eine eierlegende Wollmilchsau, die alles auf einmal macht. Das ist meistens keine gute Idee. Lass Funktionen kleine, klar abgegrenzte Aufgaben erledigen. Dateimanagement hat nicht smit dem Schreiben von Daten zu tun und erst Recht hat Benutzerinteraktion da nichts zu suchen. Aber das ist ein ganz anderes Kapitel, ich wollte es nur mal anmerken.



  • Ich danke dir erst einmal für die Antwort. Das Problem ist weiterhin genau das gleiche. Schon beim ersten Eintrag geht er in die else Verzeigung. Aus ieinem Grund ist schon iwas an Daten vorhanden?!
    Von dem cin.clear() habe ich mir erhofft, dass evtl Daten die noch vorhanden sind gelöscht werden. Ist nicht aufgegangen der Plan 😞

    Wenn es hilft, kann ich gerne mal alles Posten?

    Und vllt kannst du mir ja mal ein paar Tipps geben, wie man seine Funktionen am besten unterteilt?


  • Mod

    tomy86 schrieb:

    Ich danke dir erst einmal für die Antwort. Das Problem ist weiterhin genau das gleiche. Schon beim ersten Eintrag geht er in die else Verzeigung. Aus ieinem Grund ist schon iwas an Daten vorhanden?!
    Von dem cin.clear() habe ich mir erhofft, dass evtl Daten die noch vorhanden sind gelöscht werden. Ist nicht aufgegangen der Plan 😞

    Hast du denn mal geguckt, was cin.clear in Wirklichkeit tut? Irgendwoher musst du das ja haben. Du solltest lernen, wie du herausfindest, was Funktionen der Standardbibliothek tun, damit so etwas nicht mehr passiert. Und idealerweise lernst du dabei auch, wie du umgekehrt die passenden Funktionen finden kannst

    Wenn es hilft, kann ich gerne mal alles Posten?

    Kompilierbares Minimalbeispiel. Wobei das Problem schon klar ist: Da ist ein Zeilenumbruch im Eingabestream. Bei dem Code, den du gezeigt hast ist das nach meiner Verbesserung nicht mehr der Fall. Aber wenn dieser Code bloß Teil eines großen Programms ist, dann hilft diese Änderung natürlich nichts. Dann ist vielleicht doch etwas mit ignore angesagt.

    Und vllt kannst du mir ja mal ein paar Tipps geben, wie man seine Funktionen am besten unterteilt?

    Dem, was ich schon gesagt habe, habe ich nichts hinzu zu fügen. Klare Teilung von Aufgaben und Zuständigkeiten. Eine Funktion macht genau eine Sache (und idealerweise macht sie diese Sache dann auch gut) und nichts anderes.



  • Ja habs nochmal nach gelesen. hatte vergessen, das nur die Statusbits gelöscht werden. Was natürlich keinen Sinn macht!
    Was meinst du mit ignore angesagt?



  • Ist kein Spitzenprogramm, dient einzig und allein den Umgang mit Dateien zu lernen.

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <direct.h>
    #include <Windows.h>
    #include <limits>
    
    using namespace std;
    
    string dateiname();
    bool issetDateiname(string dateiname);
    void ladeFileSystem(string name, char lade);
    void leseDatei(string dateiname);
    void schreibeDatei(string dateiname);
    void zeigeDateien();
    void ladeMenu();
    
    void main(){
    	ladeMenu();
    }
    
    void ladeMenu(){
    	bool exit = false;
    	char menu;		
    	string name;
    	do {
    		cout << "Waehle Dateinamen  =>	W" << endl;
    		cout << "Datei schreiben    =>	S" << endl;
    		cout << "Datei ausgeben		=>	A" << endl;
    		cout << "Dateien anzeigen	=>	D" << endl;
    		cout << "Beenden			=>	B" << endl << endl;
    		cin >> menu;
    		switch(menu) {
    			case 'w':
    			case 'W':
    				system("cls");
    				name = dateiname();
    				system("cls");
    				break;
    			case 's':
    			case 'S':
    				system("cls");
    				ladeFileSystem(name, 's');
    				break;
    			case 'a':
    			case 'A':
    				system("cls");
    				ladeFileSystem(name, 'a');
    				break;
    			case 'd':
    			case 'D':
    				system("cls");
    				zeigeDateien();
    				system("pause");
    				system("cls");
    				break;
    			case 'b':
    			case 'B':
    				exit = true;
    				break;
    			default:
    				system("cls");
    				cout << "Die Eingabe ist ungueltig! Bitte versuche es erneut." << endl << endl;
    		}
    	} while(!exit);
    }
    
    string dateiname(){
    	string name;
    	cout << "Bitte gib den Dateinamen an: ";
    	cin >> name;
    	return name.append(".txt");
    }
    
    void schreibeDatei(string dateiname){
    	ofstream datei(dateiname.c_str(), ios::app);
    	if(datei) {
    		for(int i = 1; true; i++) {
    			cout << i << ". Eintrag:\n";
    			string daten;
    			if(getline(cin, daten) && daten.size()) {
    				cout << "Super" << endl;
    				datei << daten << '\n';
    			} else {
    				cout << "hmm" << endl;
    				break;
    			}
    		}
    	} else
    		cout << "Datei konnte nicht geoeffnet werden!\n";
    }
    
    void ladeFileSystem(string name, char lade){
    	if(issetDateiname(name)) {
    		if(lade == 's') schreibeDatei(name);
    		else {
    			leseDatei(name);
    			system("pause");
    		}
    		//system("cls");
    	} else {
    		cout << "Es wurde keine Datei ausgewaehlt!" << endl << endl;
    	}
    }
    
    bool issetDateiname(string dateiname){
    	if(dateiname.length() > 0) return true;
    	else return false;
    }
    
    void leseDatei(string dateiname){
    	char einlesen[100];
    
    	ifstream datei;
    	datei.open(dateiname);
    	if(datei.is_open()) {
    		while(!datei.eof()) { 
    			datei.getline(einlesen, 100);
    			cout << einlesen << endl;
    		}
    	} else
    		cout << "Die ausgewaehlte Datei existiert nicht!" << endl << endl;
    	datei.close();	
    }
    
    void zeigeDateien(){
    	char pfad[256];
    	_getcwd(pfad, 256);
    	strcat_s(pfad, "\\*");
    
    	WIN32_FIND_DATA wfd;
    	HANDLE h;
    
    	h = FindFirstFile(pfad, &wfd);
    	if(h != INVALID_HANDLE_VALUE) {
    		do {
    			string temp = wfd.cFileName;
    			if(temp.find(".txt") != -1) cout << wfd.cFileName << endl;
    		} while(FindNextFile(h, &wfd));
    		FindClose(h);
    	}
    }
    

  • Mod

    Stufe 1 der Korrektur:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <limits>
    
    using namespace std;
    
    string dateiname()
    {
      string name;
      cout << "Bitte gib den Dateinamen an: ";
      cin >> name;
      return name.append(".txt");
    }
    
    void schreibeDatei(string dateiname)
    {
      ofstream datei(dateiname.c_str(), ios::app);
      if(datei) 
        {
          cin.ignore(numeric_limits<streamsize>::max(), '\n');
          for(int i = 1; true; i++) 
            {
              cout << i << ". Eintrag:\n";
              string daten;
              if(getline(cin, daten) && daten.size()) 
                {
                  cout << "Super\n";
                  datei << daten << '\n';
                } 
              else 
                {
                  cout << "hmm\n";
                  break;
                }
            }
        } 
      else
        cout << "Datei konnte nicht geoeffnet werden!\n";
    }
    
    void leseDatei(string dateiname)
    {
      string einlesen;
    
      ifstream datei;
      datei.open(dateiname);
      if(datei) 
        {
          while(getline(datei, einlesen))
            cout << einlesen << '\n';
        } 
      else
        cout << "Die ausgewaehlte Datei existiert nicht!\n\n";
    }
    
    bool issetDateiname(string dateiname)
    {
      if(dateiname.length() > 0) return true;
      else return false;
    }
    
    void ladeFileSystem(string name, char lade)
    {
      if(issetDateiname(name)) 
        {
          if(lade == 's') schreibeDatei(name);
          else 
            {
              leseDatei(name);
            }
        } 
      else 
        {
          cout << "Es wurde keine Datei ausgewaehlt!\n\n";
        }
    }
    
    void zeigeDateien()
    {
      cout << "Dateien zeigen aus dem Verzeichnis\n";
    } 
    
    int main()
    {
      bool exit = false;
      string name;
      do {
        cout << 
          "Waehle Dateinamen  =>  W\n"
          "Datei schreiben    =>  S\n"
          "Datei ausgeben     =>  A\n"
          "Dateien anzeigen   =>  D\n"
          "Beenden            =>  B\n\n";
        char menu;
        cin >> menu;
        switch(menu) {
        case 'w':
        case 'W':
          name = dateiname();
          break;
        case 's':
        case 'S':
          ladeFileSystem(name, 's');
          break;
        case 'a':
        case 'A':
          ladeFileSystem(name, 'a');
          break;
        case 'd':
        case 'D':
          zeigeDateien();
          break;
        case 'b':
        case 'B':
          exit = true;
          break;
        default:
          cout << "Die Eingabe ist ungueltig! Bitte versuche es erneut." << endl << endl;
        }
      } while(!exit);
    }
    

    Weitere Verbesserungen folgen eventuell später, wenn ich mehr Zeit habe.



  • Wow, super ich danke dir! Allerdings werfen sich mir jetzt ein paar Fragen auf.
    1. Warum hast du alle system("cls") und system ("pause") entfernt? Sollte man die nicht benutzen? Baue sie gerne ein, damit die Konsole schön leer und übersichtlich bleibt.
    2. Hat das einen Grund, dass du anstelle von endl \n benutzt?
    3. Die Variable string name aus ladeMenu(), warum ist diese jetzt in der Schleife? Ich habe gelesen, das man Variablen die nicht zwingend in Schleifen müssen besser vorher definiert werden sollten, aus Performance Gründen. Ich weiß, in diesem Beispiel mehr als egal, aber gewöhne mir das gerne sofort an.
    4. Sollte man ignore() immer nutzen um den Puffer definitiv wieder frei zu haben, oder ist das halt nur in diesem Fall erforderlich? Und hast du zufällig auch raus bekommen woher der Doofe Zeilenumbruch kommt?
    5. Ich weiß nicht warum, aber er hat bei mit mit max() ein Problem. Bezeichner erwartet sagt er. Ich kann allerdings auch 1 als Parameter angeben und es funktioniert. Aber das sollte man denke ich nicht machen oder?
    6. <streamsize> kann ich bei numeric_limits nicht finden. Ich finde alles nur das leider nicht. Was genau bewirkt das?

    So das waren im Moment erstmal alle Fragen 😃


  • Mod

    tomy86 schrieb:

    Wow, super ich danke dir! Allerdings werfen sich mir jetzt ein paar Fragen auf.
    1. Warum hast du alle system("cls") und system ("pause") entfernt? Sollte man die nicht benutzen? Baue sie gerne ein, damit die Konsole schön leer und übersichtlich bleibt.

    Weil sie nicht portabel sind. Unter anderem funktionieren sie bei mir nicht, was doch erheblich beim Testen stören würde. Wenn du schon unportable Funktionen nutzt, kannst dusicherlich auch einfacher eine Terminalausgabe löschen, als über einen fetten Subshellaufruf. Guck malim Winapiforum oder im alten Doskonsolenforum.

    2. Hat das einen Grund, dass du anstelle von endl \n benutzt?

    Ja. endl ist '\n' und ein flush. aber ich will diese Ausgaben doch gar nicht flushen. Und i dem Moment, wo ich es möchte (vor einer Eingabe) passiert es auomatisch.

    3. Die Variable string name aus ladeMenu(), warum ist diese jetzt in der Schleife? Ich habe gelesen, das man Variablen die nicht zwingend in Schleifen müssen besser vorher definiert werden sollten, aus Performance Gründen. Ich weiß, in diesem Beispiel mehr als egal, aber gewöhne mir das gerne sofort an.

    Variablen sollte man immer so lokal wie möglich definieren. Das kann sogar die Performance steigern. Eine Ausnahme sind höchstens Objekte, die aus irgendwelchen Gründen große Kosten beider Erstellung haben (ein zu öffnender Dateistream wäre ein Beispiel). string ist ein Mittelding. Der kostet zwar nichts zu erstellen, aber es wäre hier vorstellbar, dass ein etwas weniger lokal deklarierter String hier Vorteile haben könnte, da er seinen Speicherplatz (den zu holen kostet nämlich was) eventuell wiederverwenden könnte. Allgemein würde ich die Schleife sowieso ganz anders aufbauen (dann wäre der String auch nicht lokal in der Schleife), aber ich wollte so nahe wie möglich bei deinem Programm bleiben.

    4. Sollte man ignore() immer nutzen um den Puffer definitiv wieder frei zu haben, oder ist das halt nur in diesem Fall erforderlich?

    Es gibt keinen Puffer und daher ist der auch nicht frei zu machen. Das ganze Problem erwächst letztlich dadurch, dass du hier ein Nutzeroberfläche programmieren möchtest, mit Werkzeugen, die darauf gar nicht ausgelegt sind. Die Streams sind nur abstrakte Folgen von Zeichen, die aus irgendeiner unbekannten Quelle kommen bzw. in einer unbekannten Senke verschwinden. Die tragen keine Zeitinformation, es gibt keinen Zeichenpuffer, es gibt kein Konzept von Tasten oder Konsolenausgaben. Dadurch läuft ein standardkonformes C++-Programm auch auf einem besseren Toaster, aber was du hier machen möchtest, benötigt in letzter Konsequenz eben ein GUI-Framework, um befriedigend umgesetzt zu werden. Ohne ein solches, musst du eben den Programmablauf nachvollziehen und dir überlegen, was an welcher Stelle wohl im Stream steckt.

    Und hast du zufällig auch raus bekommen woher der Doofe Zeilenumbruch kommt?

    Klar, da bestand niemals ein Zweifel. In dem Menü werden Tastaturbenutzer die Eingabe eines Buchstabens oftmals mit einem Zeilenumbruch abschließen. Da im Menü bloß ein einzelner nicht-Whitespace Buchstabe gelesen wird, ist der Zeilenumbruch hinterher noch im Stream. ein Problem mit dieser Technik ist natürlich, wenn der Benutzer keine Tastatur für die Eingaben nutzt, dann wir dieses ignore höchstwahrscheinlich eine gewünschte Eingabe für die Datei überspringen. Das ist eben das Problem, welches ich im vorherigen Absatz angesprochen habe, dass Standard-C++ keinerlei Vogaben macht, wie Eingaben überhaupt erfolgen.

    5. Ich weiß nicht warum, aber er hat bei mit mit max() ein Problem. Bezeichner erwartet sagt er. Ich kann allerdings auch 1 als Parameter angeben und es funktioniert. Aber das sollte man denke ich nicht machen oder?

    Hast du irgendwas verändert? Genaue Fehlermeldung bitte.

    6. <streamsize> kann ich bei numeric_limits nicht finden. Ich finde alles nur das leider nicht. Was genau bewirkt das?

    Das ist ein bisschen kompliziert für Anfänger zu erklären, da dieser Teil der Standardbibliothek viele Techniken nutzt, die du noch nicht kennen wirst. Meiner Meinung nach, ist numeric_limits sowieso ziemlich schlechtes Design und sollte nicht als Vorbild dienen. Das streamsize ist ein Datentyp. numeric_limits<streamsize>::max() gibt den maximalen Wert zurück, den dieser Datentyp annehmen kann. Stell dir so etwas vor wie numeric_limits<int>::max() , welches dir den maximalen Wert für einen int zurück geben würde. In vielen Fällen wird streamsize nur ein Synonym für etwas wie unsigned long int sein. Warum wollen wir das Maximum für streamsize wissen? Weil dieser Wert einen Sonderwert für das ignore darstellt. ignore(N, c) überspringt N Zeichen oder bis der Buchstabe c gefunden wird, was auch immer zuerst eintritt. Gibt man für N aber das Maximum des Datentyps streamsize an, heißt das für das ignore "beliebig viele" Zeichen zu überspringen. Es werden hier also alle Zeichen übersprungen, bis ein Zeilenumbruch erreicht wird.



  • Vielen Dank für die sehr ausführlichen Antworten! Sind sehr gute Tipps und Informationen dabei, die mir weiterhelfen.

    Zu 1: Ich wusste gar nicht, das diese nicht kompatible sind. Werde beim nächsten mal solche Dinge löschen.

    zu 3: Kannst du mir vllt mal zeigen, wie du diese Schleife aufbauen würdest? In jedem Buch, oder Tutorial habe ich Konsolenmenüs immer so aufgebaut vorgefunden. Deswegen habe ich mir das auch so eingeprägt. Sollte es bessere oder effizientere Wege geben, wüsste ich sie sehr gerne 😃

    zu 4: Ok, das die Konsole für Nutzereingaben nicht richtig geeignet ist hätte ich jetzt auch nicht gedacht. Bis jetzt kenne ich nur die Arbeit mit der Konsole. Allerdings dient auch alles nur der Vertiefung des gelesenen und da ist die Konsole der einfachste Weg denke ich.

    zu 5: Visual Studio Express 2013
    1 Warnung c4003: Nicht genügend übergebene Parameter für das Makro max
    Fehler 2 error C2589: '(': Ungültiges Token auf der rechten Seite von '::' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    Fehler 3 error C2143: Syntaxfehler: Es fehlt ')' vor '::' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    Fehler 4 error C2059: Syntaxfehler: ')' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    5 IntelliSense: Es wurde ein Bezeichner erwartet. f:\CPP Uebungen\Files\Files\Quelle.cpp 79 42 Files

    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    Hab auch versucht anstelle von streamsize int usw einzusetzen, dies hatte aber keine Auswirkung.

    zu 6: Was wäre den anstelle von numeric_limits gutes Design, bzw welche alternativen hat man? 😃
    LG



  • tomy86 schrieb:

    zu 3: Kannst du mir vllt mal zeigen, wie du diese Schleife aufbauen würdest? In jedem Buch, oder Tutorial habe ich Konsolenmenüs immer so aufgebaut vorgefunden. Deswegen habe ich mir das auch so eingeprägt. Sollte es bessere oder effizientere Wege geben, wüsste ich sie sehr gerne

    Für ein Konsolenmenü hab ich irgendwann mal eine einfach zu verwendende Klasse geschrieben. Du findest sie hier:
    https://www.c-plusplus.net/forum/303169

    Edit: Klappt nur unter Windows-Konsole



  • tomy86 schrieb:

    zu 5: Visual Studio Express 2013
    1 Warnung c4003: Nicht genügend übergebene Parameter für das Makro max
    Fehler 2 error C2589: '(': Ungültiges Token auf der rechten Seite von '::' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    Fehler 3 error C2143: Syntaxfehler: Es fehlt ')' vor '::' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    Fehler 4 error C2059: Syntaxfehler: ')' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    5 IntelliSense: Es wurde ein Bezeichner erwartet. f:\CPP Uebungen\Files\Files\Quelle.cpp 79 42 Files

    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    Hab auch versucht anstelle von streamsize int usw einzusetzen, dies hatte aber

    In windows.h wird ein Makro max definiert, das gerne zu solchen Fehlern führt. Ein Define NOMINMAX (am Besten auf der Kommandozeile) schaltet es ab.



  • Belli schrieb:

    tomy86 schrieb:

    zu 3: Kannst du mir vllt mal zeigen, wie du diese Schleife aufbauen würdest? In jedem Buch, oder Tutorial habe ich Konsolenmenüs immer so aufgebaut vorgefunden. Deswegen habe ich mir das auch so eingeprägt. Sollte es bessere oder effizientere Wege geben, wüsste ich sie sehr gerne

    Für ein Konsolenmenü hab ich irgendwann mal eine einfach zu verwendende Klasse geschrieben. Du findest sie hier:
    https://www.c-plusplus.net/forum/303169

    Edit: Klappt nur unter Windows-Konsole

    Danke für den guten Tipp. Werde das Studieren, wenn ich mich mehr mit der OOP auseinandergesetzt habe. Ist im Moment aber noch n bissl kompliziert für mich 😉

    manni66 schrieb:

    tomy86 schrieb:

    zu 5: Visual Studio Express 2013
    1 Warnung c4003: Nicht genügend übergebene Parameter für das Makro max
    Fehler 2 error C2589: '(': Ungültiges Token auf der rechten Seite von '::' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    Fehler 3 error C2143: Syntaxfehler: Es fehlt ')' vor '::' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    Fehler 4 error C2059: Syntaxfehler: ')' f:\cpp uebungen\files\files\quelle.cpp 79 1 Files
    5 IntelliSense: Es wurde ein Bezeichner erwartet. f:\CPP Uebungen\Files\Files\Quelle.cpp 79 42 Files

    cin.ignore(numeric_limits<streamsize>::max(), '\n');
    Hab auch versucht anstelle von streamsize int usw einzusetzen, dies hatte aber

    In windows.h wird ein Makro max definiert, das gerne zu solchen Fehlern führt. Ein Define NOMINMAX (am Besten auf der Kommandozeile) schaltet es ab.

    Es tut mir leid, aber ich weiß leider nicht was du genau meinst? Wie soll ich das #define NOMINMAX definieren, also was soll da fürn Wert usw rein?



  • Die Klasse kannst Du sehr einfach verwenden. In dem Posting ist ja auch ein kurzes Testprogramm!

    Du kannst die Makros min/max aus windows.h deaktivieren, indem Du VOR

    #include <windows.h>
    

    einfügst:

    #define NOMINMAX
    

    Alternativ sollte es auch funktionieren im Programm explizit die Standardbibliothek zu qualifizieren, indem man

    std::max(...
    

    anstelle von

    max(...
    

    verwendet.



  • tomy86 schrieb:

    Wie soll ich das #define NOMINMAX definieren, also was soll da fürn Wert usw rein?

    Da kommt kein Wert rein. Entweder du schreibst es einfach so

    #define NOMINMAX
    

    in die Datei oder du rufst den Compiler mit -DNOMINMAX auf.



  • Ich danke euch beiden vielmals. Jetzt läuft alles soweit erstmal.
    @Belli
    Ja, die Verwendung scheint auf dem ersten Blick recht simpel zu sein. Ich meine mit Studieren auch eher die genaue Funktionsweise 🙂
    Ich möchte ja lernen und nicht nur kopieren 😉
    Aber mein Buch ist noch nicht soweit, das die OOP zum tragen kommt. Und ich möchte mich erstmal an den Buchverlauf halten 🙂

    Aber ich hätte da doch noch mal eine Frage. Und zwar warum schaltet NOMINMAX die min, bzw max Methoden aus? Und warum nur in der windows.h und nicht ganz?



  • Noch eine Alternative zu NOMINMAX :
    `#include <windows.h>

    //...

    (std::numeric_limits<streamsize>::max)()`

    Die Klammern um std::numeric_limits<streamsize>::max verhindern dabei dass max als Function-Style Makro erkannt und expandiert wird.
    Ist nicht schön, aber in manchen Projekten wo es auf Kompatibilität ankommt eine gute Lösung.
    Weil es auch dann funktioniert, wenn windows.h schon von wo anders aus, ohne Definition von NOMINMAX inkludiert wurde.



  • tomy86 schrieb:

    Aber ich hätte da doch noch mal eine Frage. Und zwar warum schaltet NOMINMAX die min, bzw max Methoden aus? Und warum nur in der windows.h und nicht ganz?

    Das unangenehme a windows.h ist, dass es viel macros enthält (also #define) und min()/max() dort eben als macro definiert wird. NOMINMAX verhindert das



  • tomy86 schrieb:

    Und zwar warum schaltet NOMINMAX die min, bzw max Methoden aus? Und warum nur in der windows.h und nicht ganz?

    Huch?
    NOMINMAX sorgt dafür dass windows.h keine MAKROS mit den Namen min und max definiert.

    Im Prinzip steht in windows.h (sinngemäss)

    #if !defined(NOMINMAX) && !defined(min)
    #define min(a, b) ...
    #endif
    #if !defined(NOMINMAX) && !defined(max)
    #define max(a, b) ...
    #endif
    


  • coder777 schrieb:

    tomy86 schrieb:

    Aber ich hätte da doch noch mal eine Frage. Und zwar warum schaltet NOMINMAX die min, bzw max Methoden aus? Und warum nur in der windows.h und nicht ganz?

    Das unangenehme a windows.h ist, dass es viel macros enthält (also #define) und min()/max() dort eben als macro definiert wird. NOMINMAX verhindert das

    hustbaer schrieb:

    tomy86 schrieb:

    Und zwar warum schaltet NOMINMAX die min, bzw max Methoden aus? Und warum nur in der windows.h und nicht ganz?

    Huch?
    NOMINMAX sorgt dafür dass windows.h keine MAKROS mit den Namen min und max definiert.

    Im Prinzip steht in windows.h (sinngemäss)

    #if !defined(NOMINMAX) && !defined(min)
    #define min(a, b) ...
    #endif
    #if !defined(NOMINMAX) && !defined(max)
    #define max(a, b) ...
    #endif
    

    Ah ok, jetzt hab ich das Verstanden, ich danke für die Erklärungen 😃

    hustbaer schrieb:

    Noch eine Alternative zu NOMINMAX :
    `#include <windows.h>

    //...

    (std::numeric_limits<streamsize>::max)()`

    Die Klammern um std::numeric_limits<streamsize>::max verhindern dabei dass max als Function-Style Makro erkannt und expandiert wird.
    Ist nicht schön, aber in manchen Projekten wo es auf Kompatibilität ankommt eine gute Lösung.
    Weil es auch dann funktioniert, wenn windows.h schon von wo anders aus, ohne Definition von NOMINMAX inkludiert wurde.

    Sollte man nicht eh nur einmal windows,h, iostream usw inkludieren?
    Beim Visual Studio gibts ja die Möglichkeit mit vorkompilierten Headern ein Projekt zu erstellen. Da gibts dann eine Datei, in der man alle häufig benötigten Dateien inkludiert. Also z.B. iostream, windows.h usw. Oder hab ich auch da was falsch Verstanden?



  • @tomy86
    Angenommen...
    Ich entwickle eine Library "Foo" in der viel inline implementiert ist.
    Ich brauche in dieser Library std::numeric_limits<streamsize>::max() .
    Ich schreibe also

    // Foo.hpp
    #define NOMINMAX
    #include <windows.h> // Weil ich irgendwo WinAPI Funktionen brauche
    
    // ...
    
    inline void Bar()
    {
        // ...
        auto const m = std::numeric_limits<streamsize>::max();
        // ...
    }
    

    Und jemand der meine Library verwendet schreibt

    #include <windows.h>
    #include <Header File einer anderen Library welches dummerweise die min und max Macros aus windows.h braucht.h>
    #include <Foo.hpp>
    

    Dann gibt's ein Problem, und zwar weil zu dem Zeitpunkt wo windows.h das erste mal inkludiert wurde NOMINMAX nicht definiert war.
    Also wurden die min und max Makros definiert.
    Hässlicher Fix:

    #include <windows.h>
    #include <Header File einer anderen Library welches dummerweise die min und max Macros aus windows.h braucht.h>
    #undef min
    #undef max
    #include <Foo.hpp>
    

    Und eine Möglichkeit solchen Problemen aus dem Weg zu gehen, so dass der Anwender meiner Library "Foo" diesen hässlichen Workaround nicht machen muss, ist eben (std::numeric_limits<streamsize>::max)() zu schreiben.


Anmelden zum Antworten