Vortrag über Ideen für eine neue Sprache für Spieleentwickler [youtube]
-
Kellerautomat schrieb:
Und was machst du dann mit Membern?
Auch wegwerfen, wie üblich.
-
struct Foo { string s; string t; void f() { string throwaway = move(s); void(throwaway); } void g() { cout << s; } }; Foo foo; foo.f(); foo.g(); // error, objekt tot? f.s = ...; // nope f.t = ...; // ok/error?
Und was, wenn ich das Objekt wiederbenutzen will? Und wie siehts aus, wenn es gar keinen Namen fuer das Objekt gibt?
-
Du stellt Dich jetzt aber störrisch.
Members aus einem lebenden Objekt kann man nicht wegmoven.
Wenn Du es wiederbenutzen willst, dann willste es nicht wegmoven.
Wenn keinen Namen fürs Objekt gibt, ist es Dir doch egal. Der Compiler destruiert es ganz normal am Ende des Ausdrucks, außer es wurde schon im Ausdruck weggemovet.
-
volkard schrieb:
Members aus einem lebenden Objekt kann man nicht wegmoven.
Warum nicht? Unnoetige Beschraenkung.
volkard schrieb:
Wenn Du es wiederbenutzen willst, dann willste es nicht wegmoven.
Oh doch. Ein ServerSocket hat einen ClientSocket als Member und akzeptiert da Verbindungen mit. Sobald eine Verbindung da ist, wird der ClientSocket weggemoved und neu verwendet fuer die naechste Verbindung. Das ganze natuerlich Async.
volkard schrieb:
Wenn keinen Namen fürs Objekt gibt, ist es Dir doch egal. Der Compiler destruiert es ganz normal am Ende des Ausdrucks, außer es wurde schon im Ausdruck weggemovet.
vector<Movable> v; move(v[0]); v[0]; // ???
-
Kellerautomat schrieb:
volkard schrieb:
Members aus einem lebenden Objekt kann man nicht wegmoven.
Warum nicht? Unnoetige Beschraenkung.
Damit man es nicht nötog hat, überhaupt von toten Objekten auszugehen.
Wer evtl tote Objekt haben mag nimmt smartpointers oder boost::optional oder sowas.Kellerautomat schrieb:
volkard schrieb:
Wenn Du es wiederbenutzen willst, dann willste es nicht wegmoven.
Oh doch. Ein ServerSocket hat einen ClientSocket als Member und akzeptiert da Verbindungen mit. Sobald eine Verbindung da ist, wird der ClientSocket weggemoved und neu verwendet fuer die naechste Verbindung. Das ganze natuerlich Async.
Eben da nimmste smartpointers oder boost::optional oder sowas.
Kellerautomat schrieb:
volkard schrieb:
Wenn keinen Namen fürs Objekt gibt, ist es Dir doch egal. Der Compiler destruiert es ganz normal am Ende des Ausdrucks, außer es wurde schon im Ausdruck weggemovet.
vector<Movable> v; move(v[0]); v[0]; // ???
Auch nicht möglich.
-
volkard schrieb:
Eben da nimmste smartpointers oder boost::optional oder sowas.
Also eine unnoetige Allokation oder semantisch komplett falsche Klasse weil... volkard das so besser gefaellt?
volkard schrieb:
Auch nicht möglich.
Und das wird wie verhindert? Woher weiss der Compiler, ob v[0] gerade gueltig ist oder nicht?
-
Kellerautomat schrieb:
volkard schrieb:
Eben da nimmste smartpointers oder boost::optional oder sowas.
Also eine unnoetige Allokation oder semantisch komplett falsche Klasse weil... volkard das so besser gefaellt?
Nur das, was jetzt soo viele Klassen innendrin selber machen müssen, um movable zu sein.
Kellerautomat schrieb:
volkard schrieb:
Auch nicht möglich.
Und das wird wie verhindert? Woher weiss der Compiler, ob v[0] gerade gueltig ist oder nicht?
Es kann nicht ungültig sein. :p
-
audacia schrieb:
Das ist ja lustig. Und wenn die Exceptions anders hießen, würdest du dich evtl. überwinden, sie zu verwenden, anstatt sie von Hand nachzubauen?
Noe. Ich will n Feature, das die guten Faelle von Exceptions erlaubt, waehrend es die schlechten verhindert.
volkard schrieb:
Nur das, was jetzt soo viele Klassen innendrin selber machen müssen, um movable zu sein.
Also extra Arbeit, weil volkards move sowas nicht erlaubt.
volkard schrieb:
Es kann nicht ungültig sein. :p
Was soll das denn jetzt heissen? Willst du v[0] einfach ungueltige Syntax machen oder was?
Und zu dem read-Problem: Da koennte man dann eigentlich uninitialized verwenden, dann brauchst du keinen Default Ctor. Vielleicht haste dann Recht, was Entities angeht. Da kann man auch uninitialized nehmen.
-
Kellerautomat schrieb:
volkard schrieb:
Nur das, was jetzt soo viele Klassen innendrin selber machen müssen, um movable zu sein.
Also extra Arbeit, weil volkards move sowas nicht erlaubt.
Die Vorteile würden überwiegen.
Kellerautomat schrieb:
volkard schrieb:
Es kann nicht ungültig sein. :p
Was soll das denn jetzt heissen? Willst du v[0] einfach ungueltige Syntax machen oder was?
v[0] gäbe eine normale referenz zurück. also kann man das nicht wegmoven.
-
Kellerautomat schrieb:
audacia schrieb:
Das ist ja lustig. Und wenn die Exceptions anders hießen, würdest du dich evtl. überwinden, sie zu verwenden, anstatt sie von Hand nachzubauen?
Noe. Ich will n Feature, das die guten Faelle von Exceptions erlaubt, waehrend es die schlechten verhindert.
Bei "Fälle von Exceptions" geht es dir aber doch um Anwendungsszenarios, oder? Ansonsten verstehe ich nicht, wo du da in der Semantik eine Unterscheidung vornehmen willst. Kannst du das mal ausführen?
volkard schrieb:
v[0] gäbe eine normale referenz zurück. also kann man das nicht wegmoven.
Und was ist dann hier:
Movable u; Movable v[10]; move (u); // u ist fortan unberührbar move (v[rand () % 10]); // ...und v?
Gerade sowas brauche ich ja, wenn ich meinen eigenen
Vector
implementiere.
-
volkard schrieb:
v[0] gäbe eine normale referenz zurück. also kann man das nicht wegmoven.
Ok. D.h. eine normale Referenz kann man nicht moven, weil man nur Observer ist. Das ergibt sogar Sinn. Aber wie willst du dann Objekte wieder aus einem vector rausbekommen, wenn man ihn nicht mehr braucht?
-
Kellerautomat schrieb:
volkard schrieb:
v[0] gäbe eine normale referenz zurück. also kann man das nicht wegmoven.
Ok. D.h. eine normale Referenz kann man nicht moven, weil man nur Observer ist. Das ergibt sogar Sinn. Aber wie willst du dann Objekte wieder aus einem vector rausbekommen, wenn man ihn nicht mehr braucht?
resize, clear, überschreiben.
-
volkard schrieb:
resize, clear, überschreiben.
Ich meine aus Sicht des Users. Ich will das Objekt aus dem vector wieder zurueck.
-
audacia schrieb:
Bei "Fälle von Exceptions" geht es dir aber doch um Anwendungsszenarios, oder?
Korrekt.
audacia schrieb:
Ansonsten verstehe ich nicht, wo du da in der Semantik eine Unterscheidung vornehmen willst. Kannst du das mal ausführen?
Das versuche ich gerade herauszufinden. Wo ist die Linie, wo Exceptions falsch sind? Ich kann schwer komplett gegen Exceptions argumentieren, wenn ich sie selbst in manchen Faellen verwendet habe. Die Frage ist, was unterscheidet meine Faelle von denen anderer Leute? Gibt es Regeln, denen man folgen kann, um ein Programm zu schreiben, das Exceptions 100%-ig richtig verwendet? Kann man ein Feature bauen, das genau auf diese Faelle zugeschnitten ist?
-
audacia schrieb:
volkard schrieb:
v[0] gäbe eine normale referenz zurück. also kann man das nicht wegmoven.
Und was ist dann hier:
Movable u; Movable v[10]; move (u); // u ist fortan unberührbar move (v[rand () % 10]); // ...und v?
Gerade sowas brauche ich ja, wenn ich meinen eigenen
Vector
implementiere.Ich denke eher an
vector<ifstream> streams; streams.emplace_back("c:/autoexec.bat"); move(streams[0]);//geht nicht
vector<movable<ifstream>> streams; streams.emplace_back("c:/autoexec.bat"); move(streams[0]);//geht streams[0]->close();//absturz, selber schuld
ifstream in("c:/autoexec.bat"); move(in);//geht //in ist jetzt weg. in.close();//compilerfehler
-
Kellerautomat schrieb:
volkard schrieb:
resize, clear, überschreiben.
Ich meine aus Sicht des Users. Ich will das Objekt aus dem vector wieder zurueck.
Anschauen kannste es jederzeit. Rausmoven? Hmm, könnte mir sogar Algos vorstellen, wo man das machen will.
Zur Not damit keine toten Objekte im vector verbleiben müssen eine Methode.ifstream meiner=v.erase_move(rand()%10);
Und wer die Gefahr liebt
ifstream meiner=move(move_cast(v[0])); //bald kackt hier was abm außer ich repriere es schnell. new(&v[0]) ifstream("/dev/urandom");
-
Kellerautomat schrieb:
Guck dir mal an, wie sowas in Haskell gemacht wird. Das ist elegant. Stichwort Maybe/Either.
Maybe ist kein Ersatz für Exceptions, weil man damit nicht kommunizieren kann was eigentlich schief gegangen ist.
Wählst du absichtlich Beispiele die vermutlich 90% der Forenuser erst googeln müssen?
Maybe=boost::optional
Either=boost::variant oder ein beliebiger anderer Discriminated Variant Container.
Somit hätten wir den Teil "wie sowas in Haskell gemacht wird" schonmal abgehakt.
Dass speziell der Umgang mit Discriminated Variants in C++ vermutlich viel aufwendiger zu schreiben (und auch zu lesen) ist, ist wieder eine andere Sache. Geben tut es diese Mittel auf jeden Fall.
Und ich weiss auch nicht was das mit dem Thema Exceptions zu tun haben soll.
Exceptions gibt es in Haskell genau so.
Hier
http://www.haskell.org/haskellwiki/Exception
steht sogar schön beschrieben wann man Exception verwenden sollte.An exception denotes an unpredictable situation at runtime, like "out of disk storage", "read protected file", "user removed disk while reading", "syntax error in user input". These are situation which occur relatively seldom and thus their immediate handling would clutter the code which should describe the regular processing. Since exceptions must be expected at runtime there are also mechanisms for (selectively) handling them.
Lässt sich mMn. 1:1 auf C++ übertragen.
Kellerautomat schrieb:
Zu deinem std::string Beispiel sag ich einfach mal nix, da du offensichtlich meine Posts garnicht richtig gelesen hast. Du findest die Antwort dort.
Nein, finde ich nicht. Zeig sie mir.
Kellerautomat schrieb:
Exceptions verwenden kann schonmal Sinn machen - WENN man einen guten Grund dafuer hat. Aber Exceptions einfach so als universelle Loesung abzutun ist einfach nur Unsinn.
Ach, jetzt auf einmal?
BTW: der gute Grund dafür Exceptions zu verwenden steht in dem von mir gerade von haskellwiki zitierten Abschnitt beschrieben.Kellerautomat schrieb:
hustbaer schrieb:
ps:
Kellerautomat schrieb:
Es gibt so ziemlich keinen Fall, in dem ich eine Exception verwenden wuerde. (...) File konnte nicht geoeffnet werden? Das ist doch vollkommen erwartet.
"ist doch vollkommen erwartet" ist aber leider kein Argument.
Natürlich ist es erwartet. Aber warum sollte man deswegen keine Exceptions verwenden wollen/sollen/dürfen?
So lange du das nicht erklären kannst het die Feststellung "ist doch vollkommen erwartet" überhaupt keinen Bezug zum Thema.Uebersetz mal "Exception" auf Deutsch.
Ich hab fast damit gerechnet dass das jetzt kommt. Hab aber gehofft dass du intelligenter bist. Das ist nämlich wieder kein Argument. Wie das Feature heisst ist erstmal total uninteressant. Interessant ist was man damit machen kann.
Wobei es bei "File konnte nicht geoeffnet werden" genau genommen zwei Fälle zu unterscheiden gibt:
- Es ist zu erwarten dass das File existiert, und wenn es nicht existiert (oder aus anderen Gründen nicht geöffnet werden kann), dann ist es ein "unerwartetes Ereignis" das als solches behandelt werden sollte.
Den Fall hast du z.B. wenn du Asset Files in einem Spiel lädst, ein File aufmachen willst das der User in einem Open-File Dialog ausgewählt, ein Logfile mit "open or create" aufmachen willst etc. - Man weiss nicht so genau ob das File existiert bzw. ob man es aufmachen kann.
Den Fall hast du z.B. wenn du ein Config-File aufmachen willst welches aber optional ist. Gibt sicherlich noch etliche andere "Standardfälle", aber mir fällt jetzt auf die Schnelle kein weiterer ein.
Und auf (1) trifft nun genau das zu was haskellwiki über Exceptions schreibt: die Fälle kommen nicht oft vor, und man will sich seinen Code nicht damit zumüllen überall entsprechende Checks reinzuschreiben.
Und, nur weil es gerade zufällig passt: die (1) Fälle kann man auch durchaus als "Ausnahmen" bezeichnen.Was (2) angeht: dafür macht man in C++ idealerweise das selbe wie in Haskell, nämlich TryXxx Funktionen. Also z.B.
optional<File> TryOpenFile(string path, IOError& out_error)
o.Ä. Idealerweise mit einem Overload ohne den Output-Parameter, denn der aufgetretene Fehler interessiert oft gar nicht.
Du scheinst (1) aber komplett zu ignorieren, und beschränkst dich auf die wesentlich selteneren Fälle (2).Kellerautomat schrieb:
hustbaer schrieb:
Kellerautomat schrieb:
shared_ptr: Hab ich persoenlich noch nie gebraucht, kann also nicht nachvollziehen was das bringen soll. Und natuerlich kann man mit RAII use-after-free Fehler bauen. Das verhindert man am besten, indem man ein API ordentlich designed.
Uiiiiiii. Wieder kein Argument. Diesmal nicht weil der Zusammenhang fehlt, sondern weil es einfach nur eine Behauptung ist. Der ich widerspreche: geht halt nicht immer. Und je nachdem was man so programmiert kann "nicht immer" sehr oft sein.
Zeig mir die Faelle, wo du glaubst shared_ptr zu brauchen. Ich zeig dir, warum du ihn nicht brauchst.
OMG, du bist echt stur. Oder unerfahren. Oder beides.
Ein Programm kommuniziert über diverse Transport Channels (RS-232, TCP/IP Verbindung, HTTP Verbindung, ...) mit diversen Endpoints (Web-Services, Periphäriegeräte die an einem gemeinsamen Bus hängen oder verschiedenen Funktionsblöcken in diesen Geräten etc.).
Wichtig ist nur: es gibt mehrere Endpoints die über den selben Channel angesprochen werden müssen.
Gibt genügend reale Beispiele dafür, musst du mir im Zweifelsfall einfach glauben.
Und da teilen sich alle Objekte die eine Verbindung zu einem Endpoint darstellen halt einfach die Ownership des Channel Objekts.Thread-safe Signals.
Wenn dir das Problem nicht bekannt ist, dann lies dir die Doku zu Boost.Signals2 durch, da wird es ausführlich erklärt:
http://www.boost.org/doc/libs/1_56_0/doc/html/signals2/thread-safety.html#idp431602368
Der Knackpunkt ist: Das Signal muss sicherstellen dass der Slot nicht gelöscht wird während er gerade aufgerufen wird. Das Signal muss also während des Aufrufs "owner" des Slots sein. Gleichzeitig muss aber natürlich der der den Slot connected hat auch "Owner" des Slots sein, da der Slot ja sonst sofort wieder zerstört ("dicsonnected") würde.Billig kopierbare immutable Objekte mit Hilfe von
shared_ptr<T const>
bauen.
Ist ne "Optimierung", aber eine sehr gute. Ist einfach, effizient und sicher....
Kellerautomat schrieb:
Und bevor ichs vergesse, was mich an Exceptions besonders stoert, ist, dass Interfaces einem nicht verraten, ob und welche Exceptions sie werfen koennen. Wenn ich mal eine Exception vergesse zu behandeln, die dann bei mir nie auftritt, hab ich ein Problem. Vielleicht sind Javas checked Exceptions doch nicht so schlecht.
Na geht ja. DAS ist nämlich wirklich ein Argument. Wenn auch mMn. kein besonders gutes. Die Aufweichung von "kann Exceptions der folgenden Typen werfen" zu einfach nur "kann Exceptions werfen" bzw. "kann keine Exceptions werfen" ist nämlich in der Praxis sehr sehr ... praktisch.
Weil es praktisch kaum managebar ist sämtliche Exception-Typen aufzulisten die etwas werfen kann. Ob mit oder ohne Sprachsupport spielt dabei kaum eine Rolle.Was das "Exception vergessen zu behandeln" angeht: hier nähern wir uns schön langsam dem eigentlichen Problem: nämlich dass die Klassenhierarchien der Exception von Java, C++ und C# total beknackt sind, und C++ es obendrein noch erlaubt beliebige Klassen zu werfen.
Das sehe ich auch als Problem. Es führt aber nicht die ganze Exception-Sache ad Absurdum, sondern macht lediglich den Umgang mit Exceptions in C++ etwas lästig.
- Es ist zu erwarten dass das File existiert, und wenn es nicht existiert (oder aus anderen Gründen nicht geöffnet werden kann), dann ist es ein "unerwartetes Ereignis" das als solches behandelt werden sollte.
-
Zumal heute nur noch C++ eingesetzt wird, wenn es gar nicht anders geht. Wer die Wahl hat, der meidet C++ wie der Teufel das Weihwasser. Was meint ihr, wieviel von 100% Software-Entwickler haben mit C++ zu tun? Ich denke es sind nicht mehr wie 5% oder so. Die meisten arbeiten doch mit Webgeschichten oder wenn es Desktop ist mit C#, Obj-C oder Java falls es Mobil ist.
Freiwillig nimmt sich doch niemand C++ als erste Wahl, wer schiesst sich und seine Kunden denn gerne schnell freiwillig ins Bein?
-
Da muss man sich nur mal die Tiobe Index anschauen, der sagt zwar nicht viel aus, aber eine Sprache ueber die nicht oft gesprochen wird, wird auch in der Anwendung nicht mehr die grosse Rolle spielen. Immer mit einem Allzweckwerkzeug zu arbeiten ist auch wenig professionell und sehr fehleranfaellig. Was aktuell genutzt wird, kann man auch gut in den Artikeln in den Fachzeitschriften sehen, oder auch in Stellenanzeigen.
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
-
CppFewUsers schrieb:
Zumal heute nur noch C++ eingesetzt wird, wenn es gar nicht anders geht. Wer die Wahl hat, der meidet C++ wie der Teufel das Weihwasser. Was meint ihr, wieviel von 100% Software-Entwickler haben mit C++ zu tun? Ich denke es sind nicht mehr wie 5% oder so. Die meisten arbeiten doch mit Webgeschichten oder wenn es Desktop ist mit C#, Obj-C oder Java falls es Mobil ist.
Freiwillig nimmt sich doch niemand C++ als erste Wahl, wer schiesst sich und seine Kunden denn gerne schnell freiwillig ins Bein?
C# fällt weg, weil Mono unter aller Sau buggy ist. Visual Studio ist einfach lächerlich zurückgeblieben.
Apple-Müllsprachen fallen natürlich auch weg.
Java ist scheiße und tot. Es gibt keine vernünftige Entwicklungsumgebung. Soll ich ernsthaft eine Programmiersprache einsetzen, die standardmäßig Malware ("Toolbars") mitliefert?
Ich benutze C++, weil es im Gegensatz zu anderen Sprachen funktioniert. Es gibt zwei brauchbare Compiler (GCC, Clang), eine brauchbare IDE (QtCreator) und kann zur Not ohne IDE sinnvoll benutzt werden.Ich experimentiere zurzeit damit weniger Exceptions einzusetzen. Dafür habe ich ein Template
error_or<typename Value>
, das man für Rückgabetypen benutzen kann. Es ist dafür gedachtthrow system_error(..)
zu ersetzen, aber keine anderen Typen von Ausnahmen.bad_alloc
halte ich für sinnvoll, weil das sehr selten ist und kaum sinnvoll behandelt werden kann.
Ausnahmen, die nichtbad_alloc
odersystem_error
sind, halte ich in der Regel für unnötig.error_or<file_handle> open_read(path const &name) { int fd = open(name.c_str(), O_RDONLY); if (fd < 0) { return error_code(errno, system_category()); } return file_handle(fd); } int main() { //je nach Situation benutzt man das entweder so error_or<file_handle> opened = open_read("name"); if (opened.is_error()) { cerr << *opened.error() << '\n'; return 1; } //oder so file_handle opened2 = open_read("name").get(); //wirft system_error, wenn Fehler }