2012-12-12 4 views
9

Ich habe einige Probleme beim Versuch, eine benutzerdefinierte Stream-Klasse zu implementieren, um gut eingerückt Code in einer Ausgabedatei zu generieren. Ich habe online intensiv gesucht, aber es scheint keinen Konsens darüber zu geben, wie dies am besten erreicht werden kann. Einige Leute sprechen über den Strom abzuleiten, andere sprechen über den Puffer abzuleiten, noch andere schlagen die Verwendung von locales/Facetten usw.C++ benutzerdefinierte Ausgabe Stream mit Einzug

Im Wesentlichen Ich finde mich viel Code wie folgt schreiben:

ofstream myFile(); 
myFile.open("test.php"); 
myFile << "<html>" << endl << 
      "\t<head>" << endl << 
      "\t\t<title>Hello world</title>" << endl << 
      "\t</head>" << endl << 
      "</html>" << endl; 

Wenn starten die Reiter aufzuaddieren es schrecklich aussieht, und es scheint, wie es wäre schön, so etwas zu haben:

ind_ofstream myFile(); 
myFile.open("test.php"); 
myFile << "<html>" << ind_inc << ind_endl << 
      "<head>" << ind_inc << ind_endl << 
      "<title>Hello world</title>" << ind_dec << ind_endl << 
      "</head>" << ind_dec << ind_endl << 
      "</html>" << ind_endl; 

dh eine abgeleitete Stream-Klasse erstellen, die den Überblick über ihre aktuellen indent Tiefe halten würde, dann einige Manipulatoren, um den Einzug zu vergrößern/verkleinern Tiefe und ein Manipulator, um einen Zeilenumbruch gefolgt von so vielen Tabs zu schreiben.

hier ist also meine Chance auf den Klassen Umsetzung & Manipulatoren:

ind_ofstream.h

class ind_ofstream : public ofstream 
{ 
    public: 
     ind_ofstream(); 
     void incInd(); 
     void decInd(); 
     size_t getInd(); 

    private: 
     size_t _ind; 
}; 

ind_ofstream& inc_ind(ind_ofstream& is); 
ind_ofstream& dec_ind(ind_ofstream& is); 
ind_ofstream& endl_ind(ind_ofstream& is); 

ind_ofstream.cpp

ind_ofstream::ind_ofstream() : ofstream() {_ind = 0;} 
void ind_ofstream::incInd()  {_ind++;} 
void ind_ofstream::decInd()  {if(_ind > 0) _ind--;} 
size_t ind_ofstream::getInd()  {return _ind;} 

ind_ofstream& inc_ind(ind_ofstream& is)  
{ 
    is.incInd(); 
    return is; 
} 

ind_ofstream& dec_ind(ind_ofstream& is)  
{ 
    is.decInd(); 
    return is; 
} 

ind_ofstream& endl_ind(ind_ofstream& is)  
{ 
    size_t i = is.getInd(); 
    is << endl; 
    while(i-- > 0) is << "\t"; 
    return is; 
} 

Dies baut, erzeugt aber keine die erwartete Ausgabe ; Jeder Versuch, die benutzerdefinierten Manipulatoren zu verwenden, führt dazu, dass sie aus irgendeinem Grund in einen booleschen Wert umgewandelt werden und "1" in die Datei geschrieben wird. Muss ich den Operator < < für meine neue Klasse überladen? (Ich war nicht in der Lage, einen Weg zu finden, dies zu tun, das baut)

Vielen Dank!

p.s.

1) Ich habe das # includes weggelassen, Namespace usw. aus meinen Code-Schnipsel zu verwenden, um Platz zu sparen.

2) Ich möchte in der Lage sein, eine Schnittstelle ähnlich der in meinem zweiten Code-Snippet zu verwenden. Wenn Sie nach der Lektüre der ganzen Post denken, dass das eine schlechte Idee ist, erklären Sie bitte warum und bieten Sie eine Alternative an.

+1

Frage: Wenn das gewünschte Ergebnis ein sauberer Code und eine korrekte Ausgabe ist - das heißt, wenn dies nicht nur akademisch ist oder zu Ihrer eigenen Verbesserung - warum schreiben Sie [HT | X] ML direkt auf diese Weise? Zumindest könnten Sie es auf Disc UNincured schreiben, dann verwenden Sie etwas Prettifier (zum Beispiel, um diese schmutzige Arbeit zu erledigen). ... dass es interessant ist, und ich habe ein fettiges Gefühl, dass ich bald eine Lösung zusammen schmieden werde. :) – Christopher

+0

Hallo - das ist eine gute Idee und wahrscheinlich, was ich am Ende tun werde, wenn ich Plan A nicht zum Arbeiten bekommen kann. Ich finde Streams als einen der verwirrenderen Aspekte von C++, also dachte ich mir, dies könnte ein guter Weg sein, ein tieferes Verständnis dafür zu bekommen, wie sie funktionieren ... – user1898153

Antwort

8

Die iostreams unterstützen das Hinzufügen benutzerdefinierter Daten, sodass Sie keine vollständige abgeleitete Klasse schreiben müssen, nur um eine Einzugsebene hinzuzufügen, die von Manipulatoren bearbeitet wird. Dies ist eine wenig bekannte Eigenschaft von Iostreams, aber ist hier praktisch.

Sie würden Ihre Manipulatoren wie folgt schreiben:

/* Helper function to get a storage index in a stream */ 
int get_indent_index() { 
    /* ios_base::xalloc allocates indices for custom-storage locations. These indices are valid for all streams */ 
    static int index = ios_base::xalloc(); 
    return index; 
} 

ios_base& inc_ind(ios_base& stream) { 
    /* The iword(index) function gives a reference to the index-th custom storage location as a integer */ 
    stream.iword(get_indent_index())++; 
    return stream; 
} 

ios_base& dec_ind(ios_base& stream) { 
    /* The iword(index) function gives a reference to the index-th custom storage location as a integer */ 
    stream.iword(get_indent_index())--; 
    return stream; 
} 

template<class charT, class traits> 
basic_ostream<charT, traits>& endl_ind(basic_ostream<charT, traits>& stream) { 
    int indent = stream.iword(get_indent_index()); 
    stream.put(stream.widen('\n'); 
    while (indent) { 
     stream.put(stream.widen('\t'); 
     indent--; 
    } 
    stream.flush(); 
    return stream; 
} 
0

Ich habe mit einer Facette Bart van Ingen Schenau-Lösung kombiniert, zu ermöglichen, Schieben und knallend von Einrückungen zu bestehenden Ausgabeströme. Der Code ist auf GitHub: https://github.com/spacemoose/ostream_indenter, und es gibt eine gründlichere Demo/Test im Repository, aber im Grunde erlaubt es Ihnen, folgendes zu tun:

/// This probably has to be called once for every program: 
// http://stackoverflow.com/questions/26387054/how-can-i-use-stdimbue-to-set-the-locale-for-stdwcout 
std::ios_base::sync_with_stdio(false); 

std::cout << "I want to push indentation levels:\n" << indent_manip::push 
      << "To arbitrary depths\n" << indent_manip::push 
      << "and pop them\n" << indent_manip::pop 
      << "back down\n" << indent_manip::pop 
      << "like this.\n" << indent_manip::pop; 

Zur Herstellung:

I want to push indentation levels: 
    To arbitrary depths 
     and pop them 
    back down 
like this. 

Ich hatte um eine Art üblen Trick zu machen, also bin ich daran interessiert, Feedback zu dem Code-Dienstprogramm zu hören.

Verwandte Themen