Beispiel mit getopt_long(), Speichert argument nicht wenn anderes Argument ein Int anstelle eines Floats ist



  • Ich habe ein Problem in einem Testprogramm bei dem ich Programmargumente mittels getopt_long() auswerte.

    Das Problem betrifft die nachträgliche Speicherung eines zusätzlichen Parameters der nicht durch eine getopt-Option erfasst wird.
    Das konkrete Problem ist ein der Effekt das der Datentyp eines vorhergehenden Argumentes einen Einfluss darauf hat ob dieser zustäzlicher Parameter mittels eines stringstreams umgewandelt/gespeichert werden kann.

    Hier mein Beispielcode:

    #include <iostream>
    #include <string>
    #include <sstream>
    #include <getopt.h>
    
    int main(int argc, char **argv) {
      int c, intvar;
      std::istringstream iss;
      std::string str;
    
      static struct option long_options[] = {
        {"test", required_argument, 0, 0},
        {NULL, 0, NULL, 0}
      };
      int option_index = 0;
    
      while ((c= getopt_long(argc, argv, "t", long_options, &option_index)) != -1) {
        switch (c) {
          case 0:
            iss.str(optarg);
            iss >> intvar;
            break;
          default:
            std::cout<<"default";
        }
      }
      if (optind >=argc || optind < (argc-1))
        std::cout<<"more or less than one additional argument given!"<<std::endl;
      else {
          iss.str(argv[optind]);
          iss >> str;
          std::cout<< "intvar=" << intvar << ", argv[optind]="
          << argv[optind] << ", str=" << str << std::endl;
      }
    
      return 0;
    }
    

    Rufe ich das Programm mittels folgendem Kommando auf:
    ./programm --test 2.0 zusatz
    erhalte ich diese Ausgabe:
    intvar=2, argv[optind]=zusatz, str=zusatz

    wenn ich das Programm folgendermaßen aufrufe:
    ./programm --test 2 zusatz
    erhalte ich die folgende Ausgabe:
    intvar=2, argv[optind]=zusatz, str=

    Wie man sieht, wird das intvar Argument korrekt erfasst und gespeichert egal ob ich es als 2.0 oder als 2 angebe. Auch das zusätzliche Argument "zusatz" findet sich immer im letzten Eintrag des Arrays und wird auch detektiert (sonnst würde das Programm einen Fehler melden wenn optind != argc-1 wäre).

    Aber das Speichern mittels des Stringstreams in die Stringvariable str, scheitert im Fall von: "2". Das kann ich mir nicht erklären.

    Was passiert hier?



  • franko schrieb:

    Was passiert hier?

    Der stringstream erreicht eof, wenn wirklich nur ein int drin ist.
    Dann schlaegt das nächste lesen fehl.

    #include <iostream>
    #include <sstream>
    
    void print_state(const std::istream& in){
      if(in.good()){
        std::cout << "good\n";
        return;
      }
      if(in.eof())
        std::cout << "eof ";
      if(in.fail())
        std::cout << "fail ";
      if(in.bad())
        std::cout << "bad";
      std::cout << '\n';
    }
    
    int main(){
      std::istringstream iss;
      //iss.str("1");  // diese oder...
      iss.str("1.0");  // ...jene Zeile auskommentieren
      int i;
      iss >> i;
      print_state(iss);
    }
    


  • Aber in meinem Fall enthällt der Stringstream doch immer das gleiche:

    iss.str(argv[optind]);
    

    setzt den Stream gleichzeitig auch zurück.
    argv[optind] hat laut Ausgabe am Ende immer den gleichen Inhalt, in beiden Fällen! Also müsste auch der Stringstream identisch sein oder nicht?

    Die Umwanldung
    iss >> str;

    ist also unabhängig vom Einlesen des Int/Float Testargumentes, der Stream weiß garnichts davon, und sollte wie gesagt in beiden Fällen identisch sein.

    edit: laut cplusplus.com solte istringstream::str(string) den vorherigen Inahlt löschen: http://www.cplusplus.com/reference/sstream/istringstream/str/
    Zitat: " sets str as the contents of the stream, discarding any previous contents."

    Laut stackoverflow: http://stackoverflow.com/questions/2767298/c-repeatedly-using-istringstream
    scheint es aber wichtig istringstream::clear() aufzurufen bevor man den stream neu "befüllt", insbesondere um iostate flags zurückzusetzen. Die genau mein Problem vermutlich verursachen.
    Ich habe das noch nicht getestet, werde das jetzt aber direkt mal tun. Ich gehe davon aus bzw. hoffe, das mein Problem damit gelöst ist.



  • Sobald der Stream ein Fehlerbit (in diesem Fall eof) gesetzt hat, gehen nachfolgende Lesevorgänge sehr wahrscheinlich in die Hose.
    Deswegen brauchst Du das clear() (oder einen frischen Stream), ja.

    Schöner ist's allerdings gar keinen stringstream zu nehmen, weil es mittlerweile genügend Funktionen gibt, die die Konversion erledigen.
    Siehe: http://en.cppreference.com/w/cpp/string/basic_string (unter Numeric conversions)

    #include <string>
    
    void f(){
      int i = std::stoi("1");
      ...
    }
    


  • Zunächst mal, mit clear funktioniert es jetzt in der Tat.
    Was die neuen Methoden angeht, auf die muss ich leider verzichten, meine Progamme müssen leider auf rechnern Lauffähig sein die ihren Compiler und ihr OS ganz generell nur sehr seltzen updaten ("never change a running system", das gilt insbesondere für teure Großrechner die ständig von vielen Nutzer gebraucht werden). D.h. auf c++11 muss ich verzichten.


Log in to reply