enum als string ausgeben?



  • 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