Unterschiedliche Returntypen (.ini parser)?



  • Hi!

    Wenn ich zB. eine .ini haben will, mit verschiedenen Werten als Zahlen und aber auch Strings, und das ganze über eine einzige Variable ansprechen will, geht das denn zB irgendwie so?:

    // .ini Datei
    [Group1]
    Setting1 = 123
    Setting2 = abc
    [Group2]
    ...
    
    // C++ Code
    iniFile["Group1"]["Setting1"]; // Dieser Ausdruck soll ein int sein
    iniFile["Group1"]["Setting2"]; // Dieser Ausdruck soll ein std::string sein
    

    Wie müsste ich iniFile aufbauen?

    😕



  • Das ist nicht möglich. Für eine mögliche Umsetzung könntest du dir den INI-Lader von Boost.PropertyTree anschauen.


  • Mod

    Das geht mit komplizierter Template-Bastelei. zum Glück haben das schon fleißige Leute alles erledigt:
    http://www.boost.org/doc/libs/1_43_0/doc/html/variant.html



  • python nehmen



  • Boost ist mir noch zu viel.
    Kann jemand vielleicht ein Minimalbeispiel erstellen?



  • So ungefähr könnte man es machen.

    struct INI
    {
    	typedef std::map<std::string, std::string> Category;
    	std::map<std::string, Category> categories;
    
    	template <class T>
    	T get(const std::string &cat, const std::string &ent)
    	{
    		T buf = T();
    		std::stringstream ss;
    		ss.str(categories[cat][ent]);
    		ss >> buf;
    		return buf;
    	}
    
    	template <class T>
    	void set(const std::string &cat, const std::string &ent, const T &value)
    	{
    		std::stringstream ss;
    		ss << value;
    		categories[cat][ent] = ss.str();
    	}
    };
    

  • Mod

    Noobey schrieb:

    Boost ist mir noch zu viel.
    Kann jemand vielleicht ein Minimalbeispiel erstellen?

    Die Idee ist eigentlich ,dass du dadurch nicht mehr, sondern weniger Arbeit hast.





  • Scorcher24 schrieb:

    Meine Implementation eines .ini-Readers:
    http://code.google.com/p/nightlighttv/source/browse/NightLight/trunk/NightLightDLL/NLConfigManager.cpp
    http://code.google.com/p/nightlighttv/source/browse/NightLight/trunk/NightLightDLL/NLConfigManager.hpp

    Vllt hilfts Dir was.. Lizenz aber beachten wenn Du es klaust ;).
    Danke.
    rya.

    Ist sehr viel (überflüssiger) redundanter Code drinne.



  • TyRoXx schrieb:

    So ungefähr könnte man es machen.
    ...

    hmmm ... muss aber der (auslesende) Aufrufer nicht trotzdem explizit angeben, welchen Typ er erwartet?
    Auch sollte get() noch einen Mechanismus vorsehen, falls der Typ nicht konvertierbar ist (op>> fehlschlägt) ...
    ... und für "nicht-default-konstruierbare" Typen müsste man auch einiges frickeln (bzw. geeignet spezialisieren).
    Aber die Verwendung des op>> finde ich prinzipell gut.
    Vielleicht besser mit output-Parameter - dann kann man die automatische Typdeduktion nutzen und hat nicht unbedingt das Problem der def-Konstruktion (es muss nur bereits überhaupt konstruiert sein):

    template <class T>
        bool get(const std::string &cat, const std::string &ent, T& erg)
        {
            std::stringstream ss;
            ss.str(categories[cat][ent]);
            return(ss >> erg);
        }
    

    Gruß,

    Simon2.


  • Administrator

    Kann man meiner Meinung nach noch weiter verbessern:
    1. Ich würde beide Methoden anbieten (Simon2 und TyRoXx).
    2. Exceptions wären hier für den Fehlschlag besser geeignet. Sowas sollte ja wirklich eine Ausnahme darstellen.

    class ini_bad_type
      : public std::exception
    {
    private:
      std::string m_category;
      std::string m_entity;
      std::string m_value;
    
    public:
      ini_bad_type(std::string const& cat, std::string const& ent, std::string const& val)
        : m_category(cat)
        , m_entity(ent)
        , m_value(val)
      {
      }
    
    public:
      virtual char const* what() const throw()
      {
        return "ini_bad_type";
      }
    
      std::string const& get_category() const
      {
        return m_category;
      }
    
      std::string const& get_entity() const
      {
        return m_entity;
      }
    
      std::string const& get_value() const
      {
        return m_value;
      }
    };
    
    // irgendwo in einer Klasse dann...
    
    template<typename T>
    T& get(std::string const& cat, std::string const& ent, T& result) const
    {
      std::string const& value = categories[cat][ent];
      std::istringstream iss(value);
    
      // Hat jemand einen besseren Vorschlag dafür, wie man prüfen kann,
      // ob alles eingelesen wurde?
      if(iss >> result && iss.gcount() == value.length())
      {
        return result;
      }
    
      throw ini_bad_type(cat, ent, value);
    }
    
    template<typename T>
    T get(std::string const& cat, std::string const& ent) const
    {
      T result;
      return get(cat, ent, result);
    }
    

    Grüssli



  • 👍 😋

    😃

    Gruß,

    Simon2.



  • // Hat jemand einen besseren Vorschlag dafür, wie man prüfen kann, 
      // ob alles eingelesen wurde?
    

    Guck dir mal boost::lexical_cast an.
    http://www.boost.org/doc/libs/1_43_0/libs/conversion/lexical_cast.htm

    ->

    template<typename T>
    T get(std::string const& cat, std::string const& ent) const
    {
      return boost::lexical_cast<T>(categories[cat][ent]); // throws boost::bad_lexical_cast
    }
    

    Die Optimierung über T& ist zwar grundsätzlich nett, in diesem Fall aber IMO etwas deplaziert.


  • Administrator

    hustbaer schrieb:

    Guck dir mal boost::lexical_cast an.
    http://www.boost.org/doc/libs/1_43_0/libs/conversion/lexical_cast.htm

    Ich kenne boost::lexical_cast schon, verwende es ja auch regelmässig. Wollte aber nicht gleich wieder Boost einsetzen. Und ich kann es aktuell auch nicht nachschauen, wie es boost::lexical_cast macht, da ich hier grad kein Boost habe und nicht das ganze Paket runterladen wollte, nur um das nachzuschauen 😉

    Übrigens, in meiner Überprüfung, ob alles gelesen wurde, hat sich ein Fehler eingeschlichen, findet ihn jemand? 🙂

    hustbaer schrieb:

    Die Optimierung über T& ist zwar grundsätzlich nett, in diesem Fall aber IMO etwas deplaziert.

    Was genau meinst du damit? 😕

    Grüssli



  • Dravere schrieb:

    Übrigens, in meiner Überprüfung, ob alles gelesen wurde, hat sich ein Fehler eingeschlichen, findet ihn jemand? 🙂

    Hier:

    public:
      virtual ...
    

    Sowas geht gar nicht! 😃

    (Sorry, aber im Hinblick auf die andere Diskussion war die Frage zu einladend. Nur nicht ernst nehmen ;))



  • Nexus schrieb:

    Dravere schrieb:

    Übrigens, in meiner Überprüfung, ob alles gelesen wurde, hat sich ein Fehler eingeschlichen, findet ihn jemand? 🙂

    Hier:

    public:
      virtual ...
    

    Sowas geht gar nicht! 😃

    (Sorry, aber im Hinblick auf die andere Diskussion war die Frage zu einladend. Nur nicht ernst nehmen ;))

    Das nennt man self-owned. xD



  • Dravere schrieb:

    hustbaer schrieb:

    Die Optimierung über T& ist zwar grundsätzlich nett, in diesem Fall aber IMO etwas deplaziert.

    Was genau meinst du damit? 😕

    * IMO müsste hier die NRVO greifen.
    * Wenn's die nicht packt, dann Move-Semantik (aktuellen Compiler + Std. Library vorausgesetzt).
    * Was liest man aus .ini Files, was mit einem stringstream/lexical_cast konvertiert werden müsste? Fallen mir jetzt nur Typen ein die wahnsinnig billig zu kopieren sind (Integers & Floats). Bleiben noch Strings, aber die müsste man nicht konvertieren. Eine Spezialisierung für std::

    (w)string würde hier wesentlich mehr bringen als eine Variante mit Output-Parameter.
    * Ich würde eine Funktion zum Lesen von Konfigurationsdaten nicht besonders optimieren. Zumindest nicht bevor mir mein Profiler sagt dass ich es tun sollte.
    * Wenn man die Funktion wirklich unbedingt optimieren will, dann sollte man IMO den stringstream eliminieren. Was zugegebenermassen viel Arbeit ist. Die ich mir wie schon gesagt nicht antun würde, weil ich so eine Funktion gar nicht optimieren würde 🙂



  • Dravere schrieb:

    Und ich kann es aktuell auch nicht nachschauen, wie es boost::lexical_cast macht, da ich hier grad kein Boost habe und nicht das ganze Paket runterladen wollte, nur um das nachzuschauen 😉

    google "boost lexical_cast" -> Zweiter link:
    http://www.boost.org/doc/libs/1_43_0/boost/lexical_cast.hpp

    die haben die meisten (oder alle) header lesbar im Netz 🙂



  • pumuckl schrieb:

    http://www.boost.org/doc/libs/1_43_0/boost/lexical_cast.hpp

    Das ist ein Witz, oder?



  • > Das ist ein Witz, oder?

    1213 Zeilen Code für lexikalische Konvertierung? Das kann ich auch nicht nachvollziehen. Mit std::stringstream ist das ein 3-Zeiler?! 😮


  • Administrator

    hustbaer schrieb:

    Dravere schrieb:

    hustbaer schrieb:

    Die Optimierung über T& ist zwar grundsätzlich nett, in diesem Fall aber IMO etwas deplaziert.

    Was genau meinst du damit? 😕

    * IMO müsste hier die NRVO greifen.
    * Wenn's die nicht packt, dann Move-Semantik (aktuellen Compiler + Std. Library vorausgesetzt).
    * Was liest man aus .ini Files, was mit einem stringstream/lexical_cast konvertiert werden müsste? Fallen mir jetzt nur Typen ein die wahnsinnig billig zu kopieren sind (Integers & Floats). Bleiben noch Strings, aber die müsste man nicht konvertieren. Eine Spezialisierung für std::

    (w)string würde hier wesentlich mehr bringen als eine Variante mit Output-Parameter.
    * Ich würde eine Funktion zum Lesen von Konfigurationsdaten nicht besonders optimieren. Zumindest nicht bevor mir mein Profiler sagt dass ich es tun sollte.
    * Wenn man die Funktion wirklich unbedingt optimieren will, dann sollte man IMO den stringstream eliminieren. Was zugegebenermassen viel Arbeit ist. Die ich mir wie schon gesagt nicht antun würde, weil ich so eine Funktion gar nicht optimieren würde 🙂

    Verstehe ich dich richtig, dass du damit meinst, dass man die ganze Funktion weglassen soll, welche einen dritten Parameter T& hat? Das war mir nämlich noch nicht so ganz klar, ob du meinst, ob man die Funktion weglassen soll oder nur die Referenz entfernen sollte.
    Ich habe jedenfalls diese Funktion nicht hingeschrieben, weil ich an irgendeine Optimierung dachte, sondern an nicht triviale Konstruktoren. Ich muss dir aber zustimmen, dass die Chance für solche Typen eher sehr gering ist 🙂

    pumuckl schrieb:

    die haben die meisten (oder alle) header lesbar im Netz 🙂

    Ok, ok, ich gebe ja zu, ich war nur zu faul 😃

    Sie machen es übrigens einfach mit einem zusätzlichen get danach und Überprüfung auf EOF .

    typedef std::istringstream input_stream;
    typedef input_stream::traits_type input_traits;
    
    std::istringstream iss;
    
    if(iss >> result && iss.get() == input_traits.eof())
    {
      // Erfolg!
    }
    

    Der Fehler oben war übrigens, dass ich gcount verwendet habe, zum Überprüfen einer formatierten Eingabe. gcount liefert aber nur die Menge der gelesenen Zeichen der letzten unformatierten Eingabe.

    otze schrieb:

    pumuckl schrieb:

    http://www.boost.org/doc/libs/1_43_0/boost/lexical_cast.hpp

    Das ist ein Witz, oder?

    Ad aCTa schrieb:

    1213 Zeilen Code für lexikalische Konvertierung? Das kann ich auch nicht nachvollziehen. Mit std::stringstream ist das ein 3-Zeiler?! 😮

    Schaut euch mal den Code genauer an, statt nur die Anzahl Zeilen zu zählen. Ihr könnt auch gerne mal mit einem Profiler die Geschwindigkeit des 3-Zeilers in einer Funktion mit dem boost::lexical_cast vergleichen. Ich würde mal behaupten aus längst vergangenen Messungen, dass boost::lexical_cast ca. 4 mal schneller ist. Die haben zahlreiche Optimierungen vorgenommen. Wenn man sogar genau hinschaut, so wird bei Standardtypen (double, float, int, usw.) kein std::stringstream verwendet. Nicht mal irgendein IOStream aus der Standardbibliothek, sondern es werden eigene "Streams" verwendet. Der Trick ist, dass für diese Typen die maximale Grösse als Strings ausgerechnet wird und zwar zur Kompilezeit. Danach wird Speicher dafür auf dem Stack geholt und in diesen Speicher dann geschrieben. Somit musste auch eigener Code her, welcher mit dem aktuellen std::local die Zahlen formatiert oder entsprechend einliest 😉
    Und beim Cast in einen std::string , da wird dann zum Beispiel gleich der Puffer des std::string genommen, statt noch über einen anderen Puffer zu gehen. usw. usf. Lest euch selber in den Code ein 😃

    Grüssli

    PS: @Nexus, kann doch nix dafür, wenn der Standard mir das vorschreibt 😞 ... 🤡


Anmelden zum Antworten