eigene flags zu eigenen datentypen?
-
hallo
ich habe eine klasse, für welche einen operator<< (ostream, class) definiert ist. kann ich nun auch meine eigenen flags dafür definieren? und wenn ja, wie?
kurzes beispiel was ich schlussendlich möglicherweise schreiben möchte:
std::cout << mylib::decimal << data; std::ofstream file("test.dat"); file << mylib::binary << data;ich kann ja die stream-objekte (ofstream, ostream usw) ja nicht selbst bearbeiten, daher kommt mir aktuell keine gute idee dazu...
gruss
-
Geht im Prinzip genau so wie vorher. Wenn man erst einmal weiß, was dahinter steckt, ist es ganz einfach:
#include <iostream> using namespace std; ostream& manipulator_ohne_parameter(ostream& os) { // Dein Code hier. // Zum Beispiel: os << "Foo!\n"; return os; } class manipulator_mit_parameter { int data; public: manipulator_mit_parameter(int data): data(data){} friend ostream &operator<<(ostream &os, manipulator_mit_parameter man) { // Dein Code hier. // Zum Beispiel: os << "Bar(" << man.data << ")\n"; return os; } }; int main() { cout << manipulator_ohne_parameter << manipulator_mit_parameter(5); }
-
Und es ist auch richtig, dass der Parameter per value übergeben wird? In dem Fall in Zeile 17 das manipulator_mit_parameter Objekt?
-
Skym0sh0 schrieb:
Und es ist auch richtig, dass der Parameter per value übergeben wird? In dem Fall in Zeile 17 das manipulator_mit_parameter Objekt?
Was soll daran falsch sein? Es ist im Gegenteil gut, da
manipulator_mit_parameternursizeof(int)groß sein wird... man übergibt nur per Referenz bei Klassen mit teurem Kopierkonstruktor.
-
danke für die schnelle antworten.
ich bin mir nun nicht ganz sicher ob wir wirklich vom gleichen reden. ich würde ja gerne mit dem manipulator selbst noch nichts ausgeben, sondern lediglich bestimmen, wie das nächste, das ausgegeben wird, aussehen soll.ich poste mal meinen bisherigen schlechten löchrigen ansatz: (steinigt mich nicht dafür)
#include <map> #include <iostream> namespace mylib { enum visual { v_binary, v_decimal }; } namespace { ::std::map< std::ostream const*, mylib::visual> _association; } namespace mylib { template<visual _v> struct manip { const static visual v = _v; }; const manip<v_binary> binary; const manip<v_decimal> decimal; template<visual _v> ::std::ostream& operator<< (::std::ostream& os, manip<_v> const& manipulator) { ::_association[&os] = manipulator.v; return os; } class storage { char _c; public: storage(char c = 0) : _c(c) {} void set(char c) { _c = c; } char get() const { return _c; } }; ::std::ostream& operator<< (::std::ostream& os, storage const& data) { if(::_association[&os] == v_binary) { os << data.get(); } else if(::_association[&os] == v_decimal) { os << static_cast<int>(data.get()); } else { // enum-exception } return os; } } int main() { mylib::storage inst('h'); std::cout << mylib::binary << inst << '\n'; std::cout << mylib::decimal << inst << '\n'; }
-
nachtrag:
resultat:
http://ideone.com/m1yJ25stdout
h
104zusätzliche frage: wie kann ich machen, dass das verhalten definiert ist, wenn man eine instanz ausgibt ohne einen manipulator gesetzt zu haben? muss ich da für die map den allocator verändern?
-
Sone schrieb:
Skym0sh0 schrieb:
Und es ist auch richtig, dass der Parameter per value übergeben wird? In dem Fall in Zeile 17 das manipulator_mit_parameter Objekt?
Was soll daran falsch sein? Es ist im Gegenteil gut, da
manipulator_mit_parameternursizeof(int)groß sein wird... man übergibt nur per Referenz bei Klassen mit teurem Kopierkonstruktor.Nein, darum gings mir nicht. Schon klar, dass genügend kleine Daten per Value zu übergeben sind.
Ich frage nur deshalb, weil Werner vor einiger Zeit etwas mit IOStreams gepostet hat, wo ein Call-By-Value durchgeführt wurde, den ich für falsch hielt. Aber per const& kompilierte es nichtmal.
Daher die Frage, ist die Übergabekonvention egal oder macht die wirklich was aus?
Andererseits trifft SeppJ's Antwort nicht die Frage des TEs...
-
ich würde ja gerne mit dem manipulator selbst noch nichts ausgeben, sondern lediglich bestimmen, wie das nächste, das ausgegeben wird, aussehen soll.
Dafür musst du den Stream per
iword/pworderweitern. Ich poste gleich mal ein Beispiel. (Wo ist Werner, wenn man in braucht?)
-
Hier ein (schlecht konstruiertes) Beispiel:
#include <ios> struct A {}; int getFormatIPIndex() { static int const i = std::ios_base::xalloc(); return i; } enum flag_t { Hello, World }; struct setflag { flag_t flag; setflag( flag_t f ) : flag{f} {} }; #include <ostream> std::ostream& operator<<( std::ostream& os, setflag a ) { os.iword(getFormatIPIndex()) = a.flag; return os; } std::ostream& operator<<( std::ostream& os, A ) { auto index = os.iword(getFormatIPIndex()); if( index == Hello ) os << "Hello "; else if( index == World ) os << "World"; return os; } #include <iostream> int main() { std::cout << setflag(Hello) << A{} << setflag(World) << A{}; }Ideone: http://ideone.com/c5rIzM
Daher die Frage, ist die Übergabekonvention egal oder macht die wirklich was aus?
Was ausmachen im Bezug auf was? Ja, in einem Fall wird kopiert/gemoved und im anderen nicht. Auch für die Überladung macht es einen Unterschied. Aber beides ist erlaubt...
-
ahh okay, danke. genau so etwas habe ich gemeint. ich kannte das nicht mit dem xalloc, bin relativ neu bei c++.
gruss