D: C++ Streamoperator
Ich hatte gestern mal wieder mit C++ zu tun und muss sagen: ich liebe dessen Streamoperatoren. Gut bei der Ausgabe mittels cout nervt es mich schon hin und wieder, aber das einlesen von Konsoleneingaben mittels cin und vor allem bei Dateien ist es wirklich bequem und meiner Meinung nach nicht schwer zu verstehen. Vielleicht etwas kryptisch auf den ersten Blick, zugegeben, aber das legt sich bald wenn man sich erstmal daran gewöhnt hat. ![]()
Für die zukünftige Version von Dgame habe ich mir die Streamoperationen von C++ emuliert, indem ich den Shift Operator überladen habe.
Damit habe ich mir eine Input/Output sowie File Stream Emulierung gebastelt:
Input & Output:
import std.stdio; import std.conv : to; immutable(string) endl = "\n\r"; /** * Beispiel: * * --- * Output cout = new Output(); * Input cin = new Input(); * * string name; * ubyte age; * * cout << "Hallo Welt!" << endl; * cout << "Wie lautet dein Name?" << endl; * cin >> name; * cout << "Und wie alt bist du?" << endl; * cin >> age; * * writefln("My name is %s. I'm %d years old!", name, age); * * cout << "Das ist also " << name << ". Nice to meet you." << endl; * --- */ /** * Autor: Randy Schütt */ class Output { public: void output(T)(T param) const { write(param); } typeof(this) opBinary(string op, T)(T param) if (op == "<<") { this.output(param); return this; } } class Input { public: void input(T)(ref T param) const { string value = readln(); try { param = to!(T)(value[0 .. $ - 1]); } catch (Exception e) { param = cast(T)(null); } } typeof(this) opBinary(string op, T)(ref T param) if (op == ">>") { this.input(param); return this; } }
Und File Stream:
private import std.stdio; private import std.file; private import std.regex; /** * Beispiel: * * --- * File f = File.open("test.txt"); * * foreach (line; f) { * writeln(" > ", line); * } * * writeln("Erste Zeile: "); * writeln(f.readline()); * writeln("Zweite Zeile: "); * writeln(f.readline()); * * string third_line; * f >>> third_line; * writeln("Dritte Zeile: "); * writeln(third_line); * * string content; * f >> content; * * writeln("Content: "); * writeln(content); * * f << "// Abschlusszeile"; * --- */ interface Iterator { void next(); void previous(); } class File : public Iterator { private: const string _filename; string[] _lines; uint _current_line; bool _close; public: alias readlines this; static typeof(this) open(string filename) { if (!File.exists(filename)) { throw new Exception("File not found: " ~ filename); } return new this(filename); } static bool exists(string filename) { return std.file.exists(filename); } static void put_content(string filename, string content) { std.file.write(filename, content); } static string get_content(string filename) { return cast(string) std.file.read(filename); } this(string filename) { if (!File.exists(filename)) { throw new Exception("File not found: " ~ filename); } this._filename = filename; this._current_line = 0; this.open(); this.refresh(); } void open() { this._close = false; } bool closed() const { return this._close; } void close() { this._close = true; } string read() const { if (this._close) { throw new Exception("File is closed!"); } return File.get_content(this._filename); } void write(string content) { if (this._close) { throw new Exception("File is closed!"); } std.file.append(this._filename, content ~ "\r\n"); this.refresh(); } void refresh() { if (this._close) { throw new Exception("File is closed!"); } this._lines = []; string line; auto f = std.stdio.File(this._filename, "r"); while (!f.eof()) { line = f.readln(); this._lines ~= line; } f.close(); } const(string)[] readlines() const { if (this._close) { throw new Exception("File is closed!"); } return this._lines; } string readline(int line = -1) { if (this._close) { throw new Exception("File is closed!"); } if (line >= 0) { this._current_line = line; } return this._lines[this._current_line++]; } void next() { if (this._close) { throw new Exception("File is closed!"); } this._current_line++; if (this._current_line >= this._lines.length) { this._current_line = this._lines.length; } } void previous() { if (this._close) { throw new Exception("File is closed!"); } if (this._current_line > 0) { this._current_line--; } } uint current_line() const { return this._current_line; } string get_filename() const { return this._filename; } /* bool opBinary(string op, T)(T param, out int line) { switch (op) { case "in": foreach (string line; this) { auto result = std.regex.match(regex(r"" ~ param), line, "g"); if (!result.empty()) { return true; } } break; default: break; } return false; } */ typeof(this) opBinary(string op, T)(T value) if (op == "<<") { if (this._close) { throw new Exception("File is closed!"); } this.write(value); return this; } typeof(this) opBinary(string op, T)(ref T value) if (op == ">>") { if (this._close) { throw new Exception("File is closed!"); } value = this.read(); return this; } typeof(this) opBinary(string op, T)(ref T value) if (op == ">>>") { if (this._close) { throw new Exception("File is closed!"); } value = this.readline(); return this; } }
Vielleicht hat ja jemand Interesse und/oder Spaß daran.
Referenz oder doch Zeiger?
In C oder C++ würde man stets einen Zeiger übergeben, sofern man in der zu übergebenen Methode/Funktion diesem Parameter einen neuen Wert zuweisen würde. Denn ansonsten würde dieser neu zugewiesene Wert am Ende der Methode/Funktion wieder verfallen.
Es ist nicht einmal so, dass man das tun würde, sondern das man es tun muss, da man in C/C++ keine andere Wahl hat.
In D und auch in C# hat man dafür das Keyword “ref”, wobei man in D dennoch noch Zeiger benutzen kann.
Bei einem Benchmark, welches denn nun schneller sei, stellte ich fest, dass kaum merkbare Unterschiede festzustellen sind und ref als Syntax wirklich angenehmer wirkt.
Doch wofür dann noch, außer natürlich für C Kompatibilität, in D Zeiger benutzen?
More »