enum als string ausgeben?
-
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_mapsieht 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.

-
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::arraygleich demboost::array?
Dann gibt es nämlich Probleme, allerdings mehr in der Bequemlichkeit.
boost::arrayhat 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::mapeher denstd::stringnehmen. 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::arraygleich demboost::array?
Dann gibt es nämlich Probleme, allerdings mehr in der Bequemlichkeit.
boost::arrayhat keine KonstruktorenDafü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::mapeher denstd::stringnehmen. 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/03Gruß,
Valentin Heinitz