{"id":2831,"date":"2017-03-11T12:49:55","date_gmt":"2017-03-11T11:49:55","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=2831"},"modified":"2017-03-11T12:55:20","modified_gmt":"2017-03-11T11:55:20","slug":"c17-guns-stdoptional-part-iii","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=2831","title":{"rendered":"C++17 Guns - std::optional Part III"},"content":{"rendered":"<p>So, kommen wir zum dritten Teil. Zu Versuchs und Lernzwecken habe ich meine eigene cpl::optional Klasse geschrieben, die genau das macht, was ich zur Zeit brauche. Ich mache mir absichtlich das Leben einfach und vordere, dass der Typ default constructible sein muss. H\u00e4lt cpl::optional kein Wert, so ist ein default Wert gespeichert. Das hat die schon beschriebenen Nachteile, aber man muss ja pragmatisch denken. Die Syntax soll nicht so einfach wie m\u00f6glich sein, sondern so einfach, wie sie vorher schon war. Zum Vergleich zeige ich den alten Code, eine funktionale Version und eine neue Version<\/p>\n<p>Alter Code:<\/p>\n<pre><code>\r\n    bool hasDMP = false;\r\n    Punkt DMP;\r\n    bool hasSMP = false;\r\n    Punkt SMP;\r\n    for(const Punkt& punkt : schacht.punkte) {\r\n        if(punkt.attr == PunktAttributAbwasser::DMP) {\r\n            hasDMP = true;\r\n            DMP = punkt;\r\n        } else if(punkt.attr == PunktAttributAbwasser::SMP) {\r\n            hasSMP = true;\r\n            SMP = punkt;\r\n        }\r\n    }\r\n\r\n    if(hasDMP and hasSMP) {\r\n        if(fuzzyCompare(abs(DMP.point.z-SMP.point.z), schacht.tiefe) == false) {\r\n            cout << \"Redundante Information Schachttiefe vs DMP-SMP\";\r\n        }\r\n    }\r\n<\/code><\/pre>\n<p>Diese Version hat folgende Nachteile und sleeping errors (Phew wo fang ich denn an).<br \/>\na) WAS vs. WIE. In dem Code steht, WIE die zwei Punkte gefunden werden und danach WIE damit weiter verfahren wird. Aber es steht nicht da WAS \u00fcberhaupt gemacht werden soll.<br \/>\nb) SMP und DMP sind \u00e4hnlich geschriebene Variablen die leicht verwechselt werden k\u00f6nnen. Das sind aber vorgegebene Abk\u00fcrzungen die es einzuhalten gilt. Viel schlimmer aber sind die beiden zus\u00e4tzlichen bool Variablen. Es k\u00f6nnen die bool und Punkt Variablen in jeder Kombination vertauscht werden.<br \/>\nc) Die Lebenszeit der DMP und SMP Variablen ist zu lange. Nach der letzten if() geht der Code normalerweise noch weiter. Hier k\u00f6nnte man eine der Variablen wieder benutzen, obwohl das gar nicht beabsichtigt ist. Ein \u00e4hnliches Problem hat die Sprache C mit der Laufvariable i in for() Schleifen. Das ist seit 1984 in C++ nie wieder ein Fehler gewesen.<br \/>\nd) RAII nicht beachtet. Erst wird der Punkt default initialisiert, und sp\u00e4ter vllt. einem anderen Punkt aus dem Schacht zugewiesen. Statt gleich die Zuweisung zu machen.<br \/>\ne) Und letztendlich wird eine Kopie des Punktes erzeugt. Das ist in diesem Fall aber das kleinere \u00dcbel. Referenzen, Indices, Iteratoren oder Pointer k\u00f6nnen alle invalid werden, sollte sich die Punkte in dem Schacht zwischenzeitlich \u00e4ndern.<\/p>\n<p>Hier sind L\u00f6sungsvorschl\u00e4ge:<br \/>\na) Die Schleife muss in eine Funktion ausgelagert werden.<br \/>\nb) Punkt und bool Variable in ein neuen Datentyp koppeln.<br \/>\nc) Mehrere M\u00f6glichkeiten:<br \/>\nScope durch zwei Klammern {} verkleinern.<br \/>\nCode in eine weitere Funktion packen.<br \/>\nC++17 \"Selection statements with initializer\" nutzen.<br \/>\nd) Auch (teil)gel\u00f6st durch Schleife in Subroutine.<\/p>\n<p>Punkt a) ist trivial. Das sollte etwa so aussehen: auto DMP = punkt.DMP();<br \/>\nPunkt b) ist durch ein optional l\u00f6sbar. Ich zeige Code mit std::optional und cpl::optional.<br \/>\nPunkt c) C++17 ist nat\u00fcrlich vorzuziehen.<br \/>\nPunkt d) Da cpl::optional default constructible brauchst ist hier RAII nicht so zu 100% enigehalten.<\/p>\n<p>Fangen wir an:<\/p>\n<pre><code>\r\ncpl::optional&ltPunkt> Schacht::DMP() const {\r\n    for(const Punkt& punkt : punkte) {\r\n        if(punkt.attr == PunktAttributAbwasser::DMP) {\r\n            return punkt;\r\n        }\r\n    }\r\n\r\n    return {};\r\n}\r\n<\/code><\/pre>\n<p>Im Erfolgsfall wird der Punkt zur\u00fcck gegeben, sonst ein default cpl::optional, welches keine Daten h\u00e4lt. F\u00fcr SMP() analog. Um zu testen ob ein std::optional ein Wert h\u00e4lt, gibt es die Funktionen has_value() oder operator bool. Ich m\u00f6chte gern die Funktion beim Namen nennen, um den Code lesbarer zu halten. Aber punkt.has_value() ist.. seltsam. Jeder Punkt hat doch Werte? Daher nehme ich f\u00fcr cpl::optional lieber den Funktionsnamen exist(). Und so schauts aus:<\/p>\n<pre><code>\r\nauto DMP = schacht.DMP();\r\nauto SMP = schacht.SMP();\r\n\r\nif(DMP.exist() and SMP.exist()) {\r\n    if(fuzzyCompare(std::abs(DMP.point.z-SMP.point.z), schacht.tiefe) == false) {\r\n        cout << \"Redundante Information Schachttiefe vs DMP-SMP\";\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Das ist schon wesentlich lesbarer. Hier empfiehlt sich die NUtzung von \"auto\", sonst m\u00fcsste man umst\u00e4ndlich cpl::optional<Punkt> schreiben. Also, Punkt a) b) d) erf\u00fcllt. F\u00fcr c) nehme ich C++17, das sieht dann so aus:<\/p>\n<pre><code>\r\nif(auto DMP = schacht.DMP();  DMP.exist()) {\r\n    if(auto SMP = schacht.SMP(); SMP.exist()) {\r\n        if(fuzzyEqual(std::abs(DMP.point.z-SMP.point.z), schacht.tiefe) == false) {\r\n            std::cout << \"Redundante Information Schachttiefe vs DMP-SMP\";\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Jetzt bin ich mir nur nicht sicher, ob die Initialisierung in der if() auch f\u00fcr mehrere Variablen funktioniert, oder ob der Compiler noch buggy ist. Aber jedenfalls, aus den anf\u00e4nglichen 19 Zeilen Code sind es 7-8 geworden. Ein WIE wurde in ein WAS verwandelt. Der zweite Teil mit dem fuzzyEqual lass ich mal so stehen.<\/p>\n<p>Hier die Implementierung von cpl::optional<\/p>\n<pre><code>\r\nnamespace cpl {\r\ntemplate&ltclass T>\r\nstruct optional : public T {\r\n    static_assert(is_default_constructible&ltT>::value);\r\n    optional() = default;\r\n\r\n    optional(const T& t)\r\n        : T(t), vorhanden(true)\r\n    {\r\n\r\n    }\r\n\r\n    explicit operator bool() const {\r\n        return vorhanden;\r\n    }\r\n\r\n    bool exist() const {\r\n        return vorhanden;\r\n    }\r\n\r\n    bool vorhanden = false;\r\n};\r\n}\r\n\r\n<\/code><\/pre>\n<p>Das n\u00e4chstemal geht es mit std::optional weiter<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So, kommen wir zum dritten Teil. Zu Versuchs und Lernzwecken habe ich meine eigene cpl::optional Klasse geschrieben, die genau das macht, was ich zur Zeit brauche. Ich mache mir absichtlich das Leben einfach und vordere, dass der Typ default constructible sein muss. H\u00e4lt cpl::optional kein Wert, so ist ein default Wert gespeichert. Das hat die [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[17],"class_list":["post-2831","post","type-post","status-publish","format-standard","hentry","category-allgemein","tag-cpp"],"_links":{"self":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/2831","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2831"}],"version-history":[{"count":8,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/2831\/revisions"}],"predecessor-version":[{"id":2842,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/2831\/revisions\/2842"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2831"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2831"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2831"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}