enum als string ausgeben?



  • apropos: geht so was eigtl iwie?

    std::map <const char[3], size_t> test;
    void Init ()
    	{
    		test.insert (std::make_pair <const char[3], size_t> ("asd", 0));
    	}
    

    Ich will immer ganz genau 3 Buchstaben und nen Wert dazu... Und will aber auch an hand des size_t elements wieder an die 3 Buchstaben kommen - ist da überhaupt std::map die richtige Wahl?

    Danke



  • Ja. Das geht.
    Aber da würde ich eher zu sowas raten:

    std::vector<string> vec;
    vec.push_back ("aab");
    vec.push_back ("bba");
    
    std::cout << vec[1];
    

    Ich würde dir sowieso zu string raten. Du hast da nämlich bereits einen typischen C-String Fehler drin.



  • naja... aber ich weiß ja, dass es immer 3 zeichen sind - deshalb brauch ich die terminierende 0 ja eigtl nicht - aber kA, wie ich bei "asd" die 0 weg bekomme - und 'a', 's', 'd' wird ja vermutlich nicht gehen?

    zur erklärung:
    es sind die stati eines objektes - die empfange ich aber (via netzwerk) als string mit immer exakt 3 zeichen länge...
    wobei mir gerade einfällt, dass ich die 3 zeichen ja vll auch auf nen DWORD oder so casten kann? aber dann speicher ich halt 4 byte obwohl es nich mal genug stati gibt um 1 byte nicht als verschwendung anzusehen ^^

    bis jetzt habe ich nen ganz normales enum - aber so müsst ich ja jedes mal wieder tippen if (string[0] == 'X' && string[1] == 'Y' && string[3] == 'Z')
    das suckt alles bissl ><

    alternative Vorschläge?

    bb



  • Wie gesagt. Nutzt strings und pack das ganze in einen Container. Dann kannst du da auch ganz bequem drin suchen.



  • asdasdasdasdasd schrieb:

    apropos: geht so was eigtl iwie?

    std::map <const char[3], size_t> test;
    void Init ()
    	{
    		test.insert (std::make_pair <const char[3], size_t> ("asd", 0));
    	}
    

    ...

    Hmmm, welchen Vergleich verwendet die map-Implementation bei sowas eigentlich ?

    asdasdasdasdasd schrieb:

    ...
    Ich will immer ganz genau 3 Buchstaben und nen Wert dazu... Und will aber auch an hand des size_t elements wieder an die 3 Buchstaben kommen - ist da überhaupt std::map die richtige Wahl?...

    Also in der map kann man nach dem value-Typ (hier size_t) nicht genauso effizient suchen wie nach dem key-Typ (hier char const[3]), weswegen (multi)map eine gute Wahl ist, wenn man nach einem von beiden deutlich häufiger sucht als nach dem Anderen (und auch häufiger sucht als einfügt/löscht).
    Muss man halt mal sehen, ob der Performancemäßig eine Rolle spielt....

    Gruß,

    Simon2.



  • asdasdasdasdasd schrieb:

    apropos: geht so was eigtl iwie?

    std::map <const char[3], size_t> test;
    void Init ()
    	{
    		test.insert (std::make_pair <const char[3], size_t> ("asd", 0));
    	}
    

    Nein, geht nicht. Weil bei der Übergabe von make_pair an pair die Größeninformation verlorengeht, da das Array nicht als Referenz übergeben wird. Damit wird ein std::pair<char const[3],size_t> mit einem char const* initialisiert.

    Selbst wenn man das umgeht, indem man statt make_pair direkt den pair-Konstruktor nutzt, müssten in eben diesem zwei Arrays einander zugewiesen werden, was der Standard verbietet.

    (EDIT: Mit dem für "asd" korrekten Typ char const[4] gilt übrigens dasselbe. Bevor jemand schreit :D)



  • Simon2 schrieb:

    asdasdasdasdasd schrieb:

    apropos: geht so was eigtl iwie?

    std::map <const char[3], size_t> test;
    void Init ()
    	{
    		test.insert (std::make_pair <const char[3], size_t> ("asd", 0));
    	}
    

    ...

    Hmmm, welchen Vergleich verwendet die map-Implementation bei sowas eigentlich ?

    Gruß,

    Simon2.

    würde sagen : operator <, und dann wohl einfach die pointer-werte (adressen)
    also nichts mit strcmp, das wäre dann auch effizienter als operator < von
    nem string



  • Treb schrieb:

    Simon2 schrieb:

    Hmmm, welchen Vergleich verwendet die map-Implementation bei sowas eigentlich ?

    operator <

    Korrekt. map arbeitet auf einer Äquivalenzrelation ("strict weak ordering"). Gleichheit wird definiert über !(a<b) && !(b<a) . Beim Definieren eines eigenen operator< für die Verwendung von map muss man peinlich genau auf die Einhaltung der Bedingungen eines strict weak ordering achten, da map (und set) sonst nicht richtig funktionieren kann.



  • unskilled schrieb:

    es sind die stati eines objektes

    Der Plural von „status“ ist übrigens „status“ mit einem langen zweiten „u“: u-Deklination. (Und ja, auch auf Deutsch.)

    </klugscheiss>



  • Treb schrieb:

    Simon2 schrieb:

    ...
    Hmmm, welchen Vergleich verwendet die map-Implementation bei sowas eigentlich ?

    Gruß,

    Simon2.

    würde sagen : operator <, und dann wohl einfach die pointer-werte (adressen)...

    Das hätte ich auch mal vermutet ... weswegen ich auch dachte, dass das obige Konstrukt eben nicht (im erwarteten Sinne) "geht".

    7H3 N4C3R schrieb:

    ...Beim Definieren eines eigenen operator< für die Verwendung von map muss man peinlich genau auf die Einhaltung der Bedingungen eines strict weak ordering achten, da map (und set) sonst nicht richtig funktionieren kann.

    Das war mir durchaus bekannt - aber mir ist eben nicht klar, ob es std::map-Implementierungen nicht erlaubt wäre, entweder einen entsprechenden operator<() zu definieren (zumindestens für NTBS) oder eine passenden Spezialisierung für char* anzubieten, die mit geeignetem Vergleich arbeitet...

    Gruß,

    Simon2.



  • enum2str kann man sich ja selber basteln. Mit Hilfe von defines geht es auch relativ simpel:

    #include <iostream>
    
    #define ENUM2STR_BEGIN(VAR) switch( VAR ){
    #define ENUM2STR(EN) case EN: return std::string( #EN );
    #define ENUM2STR_END(ERR) }; return std::string( ERR );
    
    enum Horst{
        Uwe,
        Klaus,
        Peter
    };
    
    std::string Enum2Str_Horst(Horst h){
        ENUM2STR_BEGIN(h);
        ENUM2STR(Uwe);
        ENUM2STR(Klaus);
        ENUM2STR(Peter);
        ENUM2STR_END("unknown enum");
    }
    
    int main(int argc, char *argv[]){
        Horst h = Uwe;
        std::cout << Enum2Str(h) << std::endl;
    }
    

    Vermutlich gibt es noch ein paar Makros mit denen das einfacher geht, vielleicht auch nicht. *shrug*



  • Simon2 schrieb:

    Das war mir durchaus bekannt - aber mir ist eben nicht klar, ob es std::map-Implementierungen nicht erlaubt wäre, entweder einen entsprechenden operator<() zu definieren (zumindestens für NTBS) oder eine passenden Spezialisierung für char* anzubieten, die mit geeignetem Vergleich arbeitet...

    Ah achso. 🙂 Als drittes Template-Argument kannst du an map eine Komparator-Klasse angeben, die die Vergleiche macht. Default ist meine ich std::less<Key>



  • Ist die Betrachtung nicht müßig, wenn's sowieso nicht geht? 😉



  • LordJaxom schrieb:

    Ist die Betrachtung nicht müßig, wenn's sowieso nicht geht? 😉

    Think encapsulation. Mit einem std::tr1::array<char, 3> sollte es doch wunderbar gehen, oder übersehe ich da etwas?

    Das ist zwar eine seehr voreilige Optimierung aber mir fallen als Bioinformatiker spontan mindestens zwei Anwendungen ein, in denen das wirklich Sinn ergeben würde (nein, tut's nicht, weil 'map' keine Hashtabelle ist, mit der std::tr1::unordered_map sieht die Sache aber schon anders aus).



  • Hast recht, wobei in diesem Fall wieder ein Benutzerdefinierter Operator für den Array greifen kann (was bei nicht-UDTs ja nicht geht). Ich dachte darum ging es bei der Nebenfrage in erster Linie.



  • Fellhuhn schrieb:

    enum2str kann man sich ja selber basteln. Mit Hilfe von defines geht es auch relativ simpel:

    #include <iostream>
    
    #define ENUM2STR_BEGIN(VAR) switch( VAR ){
    #define ENUM2STR(EN) case EN: return std::string( #EN );
    #define ENUM2STR_END(ERR) }; return std::string( ERR );
    
    enum Horst{
        Uwe,
        Klaus,
        Peter
    };
    
    std::string Enum2Str_Horst(Horst h){
        ENUM2STR_BEGIN(h);
        ENUM2STR(Uwe);
        ENUM2STR(Klaus);
        ENUM2STR(Peter);
        ENUM2STR_END("unknown enum");
    }
    
    int main(int argc, char *argv[]){
        Horst h = Uwe;
        std::cout << Enum2Str(h) << std::endl;
    }
    

    Vermutlich gibt es noch ein paar Makros mit denen das einfacher geht, vielleicht auch nicht. *shrug*

    Das funktioniert aber nicht, wenn man mehrere Enum-Flags in einer Variablen hat, was doch sehr häufig der Verwendungszweck von Enums ist. Wobei du es leicht ändern könntest, so dass es alle benutzen Flags zurückgibt.



  • Dann ist es aber auch kein "gültiger" Enum-Wert mehr, sondern eben eine Kombination eben jener. Und darum ging es nicht. 😛


  • Administrator

    Konrad Rudolph schrieb:

    Think encapsulation. Mit einem std::tr1::array<char, 3> sollte es doch wunderbar gehen, oder übersehe ich da etwas?

    Ist das std::tr1::array gleich dem boost::array ?
    Dann gibt es nämlich Probleme, allerdings mehr in der Bequemlichkeit.
    boost::array hat keine Konstruktoren, wodurch jegliche implizite Umwandlung unmöglich ist. Desweiteren haben mir noch keine rohen Strings, sondern alle sind noch mit einem '\0' abgeschlossen. Bsp:

    #include <boost/array.hpp>
    
    #include <map>
    #include <string>
    
    int main()
    {
      typedef boost::array<char, 3> Array_t;
      typedef std::map<Array_t, int> FirstMap_t;
      typedef std::map<std::string, int> SecondMap_t;
    
      Array_t tempArray;
      FirstMap_t firstMap;
      SecondMap_t secondMap;
    
      // Zuerst mit dem Array:
      char const* p = "abc";
      tempArray.assign(p, p + 3); // Unter VS noch warnings ...
      firstMap[tempArray] = 0; // zusätzliche Kopie!
    
      // usw.
    
      // Dann mit dem std::string:
      secondMap["abc"] = 0; // irgendwie angenehmer, nicht?
    
      return 0;
    }
    

    Zudem wenn man noch bedenkt, was für Optimierungen der Compiler machen kann, dann würde ich persönlich bei einer std::map eher den std::string nehmen. Alles andere ist Mikrooptimierung, welche allenfalls später durchgeführt werden kann. Kann mir aber schlecht vorstellen, dass es dadurch einen Geschwindigkeitsverlust geben wird.

    Grüssli



  • Dravere schrieb:

    Konrad Rudolph schrieb:

    Think encapsulation. Mit einem std::tr1::array<char, 3> sollte es doch wunderbar gehen, oder übersehe ich da etwas?

    Ist das std::tr1::array gleich dem boost::array ?
    Dann gibt es nämlich Probleme, allerdings mehr in der Bequemlichkeit.
    boost::array hat keine Konstruktoren

    Dafür aber Array-Initializer, die hier evtl. sowieso besser geeignet sind.

    typedef array<char, 3> tlc_t;
    typedef map<tlc_t, char, tlc_comp> tlc_to_aa_t;
    
    tlc_t a = {{ 'a', 'l', 'a' }};
    tlc_t g = {{ 'g', 'l', 'y' }};
    // … etc.
    
    tlc_to_aa_t tr;
    tr[a] = 'a';
    tr[g] = 'g';
    // … etc.
    

    Zudem wenn man noch bedenkt, was für Optimierungen der Compiler machen kann, dann würde ich persönlich bei einer std::map eher den std::string nehmen. Alles andere ist Mikrooptimierung, welche allenfalls später durchgeführt werden kann. Kann mir aber schlecht vorstellen, dass es dadurch einen Geschwindigkeitsverlust geben wird.

    Ha. Na ja. In unserem Bioinformatik-Software-Framework haben wir für alle möglichen Konvertierungen hartcodierte Übersetzungstabellen und die bringen definitiv einen Geschwindigkeitsvorteil. Aber zugegeben, hier sind solche Dinge auch wirklich wichtig.



  • Der Beitrag ist nicht ganz frisch, trotzdem wollte ich meinen Vorschlag auch posten. Ich habe für enum-string Konvertierung ein Perl-Skript geschrieben, das eine Klasse mit vielen überladenen toString-Methoden erzeugt.
    Die C++ Dateien werden nicht mit Perl geparst sondern es wird zuvor die tags-Datei mit dem Tool ctags erzeugt.
    Hier ist das Skript:

    #!/bin/perl
    # Valentin Heinitz, 2010-03-20
    # Script for making C++ class which converts enum to string.
    # Version: 0.01
    # This script is Public Domain. Copy, modify and redistribute it as you want.
    # The software is provided "as is", without warranty of any kind.
    #
    # Call it from Source-code root-directory with command-line:
    # perl enum2string.pl > <ToStringClassFileName.h>
    use strict;
    use warnings;
    #Found enum types will be added to this list
    my @etypes=();
    #Found includes will be added to this list
    my @includes=();
    
    # Pattern for searching enum types and corresponding include-file.
    my $pat="^[^\t]+\t([^\t]+)\t.*enum:([a-zA-Z_][a-zA-Z0-9_]*)";
    my $file = "tags";
    
    system "ctags -R *";
    
    open T, ">tmp" or die "couldn't open tmp\n";
    open F, $file or die "couldn't open $file\n";
    
    #Replacing MS-Windows paths by UNIX paths (for includes)
    while (<F>) {
     $_ =~ s/\\/\//;
     print T "$_";
    }
    close F;
    close T;
    
    $file="tmp";
    
    open F, $file or die "couldn't open $file\n";
    while (<F>) {
      if (my ($m) = m/$pat/){
        $etypes[++$#etypes] = $2;
        $includes[++$#includes] = $1;
        #print "$_\n";
      }
    }
    close F;
    
    #Class name to be generated. Change as you like, or set from argument
    my $clName = 'EnumToString';
    
    #Remove duplicates from enum type list
    my %hlp1 = ();
    my @uniqenums = grep { ! $hlp1{$_} ++ } @etypes;
    
    #Remove duplicates from include list
    my %hlp2 = ();
    my @uniqinc = grep { ! $hlp2{$_} ++ } @includes;
    
    print "#ifndef _H_G__$clName\n";
    print "#define _H_G__$clName\n\n";
    print "#include <iostream>\nusing std::cout;\n";
    
    foreach my $inc (@uniqinc)
    {
      print "#include \"$inc\"\n";
    }
    
    print "\n\n";
    
    print "class $clName {\n";
    print "  EnumToString(); // utility class, no instances\n\n";
    print "public:\n";
    
    foreach my $k (@uniqenums)
    {
      $pat = "^([a-zA-Z0-9_]*)\t.*e\tenum:$k";
      print "  static const char * toString( $k en ) {\n";
      print "    switch( en ) {\n";
      open F, $file or die "couldn't open $file\n";
      while (<F>) {
        if (my ($m) = m/$pat/){
          print "      case $1: return \"$1\";\n";
        }
      }
      close F;
      print "    }\n";
      print "  }\n\n";
    
    }
    
    #Test
    print "static void testEnum2String() {\n";
    foreach my $k (@uniqenums)
    {
      $pat = "^([a-zA-Z0-9_]*)\t.*e\tenum:$k";
      print "  std::cout << \"Enums of $k:\\n\";\n";
      open F, $file or die "couldn't open $file\n";
      while (<F>) {
        if (my ($m) = m/$pat/){
          print "  std::cout <<\"   \"<<  toString( $1 ) <<\" -> \" << $1 <<\"\\n\";\n";
        }
      }
      close F;
    }
    print "  }\n\n";
    
    print "};\n\n";
    print "#endif\n";
    

    Die komplete Beschreibung mit dem Beispielprojekt ist auf meiner Webseite zu finden:
    http://heinitz-it.de/archives/date/2010/03

    Gruß,
    Valentin Heinitz


Anmelden zum Antworten