{"id":3379,"date":"2018-03-23T02:31:13","date_gmt":"2018-03-23T01:31:13","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=3379"},"modified":"2018-03-25T20:14:46","modified_gmt":"2018-03-25T19:14:46","slug":"c-guns-concept-force-function-return-type","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=3379","title":{"rendered":"C++ Guns: concept: force function return type"},"content":{"rendered":"<p>Das Problem von impliziten Konversionen in C\/C++ ist nicht neu. Wenn integer in floats und floats in integer kopiert werden und dabei die Werte der Zahlen ver\u00e4ndert werden, kann das in wissenschaftlichen Anwendungen katastrophale Auswirkungen haben. Es m\u00fcssen verschiedene Szenarien beachtet werden. So ist z.b. das kopieren eines 32bit integer in ein 64bit float kein Problem. Hingegen kann ein 32bit integer nicht ohne Verlust in ein 8bit integer gespeichert werden. Au\u00dfer, es ist vom Algorithmus her unm\u00f6glich, dass sich die Wertebereiche \u00fcberlappen.<\/p>\n<p>Ich werde versuchen das Thema an Hand von Beispielen zu verdeutlichen. Sowohl f\u00fcr Funktions-Parameter als auch f\u00fcr R\u00fcckgabe Typen. Diverse Compilerparameter die man immer nutzen sollte, sowie ob Concepts hier helfen k\u00f6nnen.<\/p>\n<p>Beispiel 1:<br \/>\nDrei Member Funktionen die jeweils ein bool \u00fcbergeben, und bool zur\u00fcck geben sollen. Aber der Programmierer hat sich vertan und \u00fcbergibt einmal ein integer und float. Im zweiten Fall wird ein Integer zur\u00fcck gegeben und im dritten Fall ist der Return Type falsch gew\u00e4hlt, aber es wird dennoch ein bool zur\u00fcck gegeben.<\/p>\n<p>Da eine implizite Umwandlung zwischen bool und integer\/float erlaubt ist, erwarte ich nicht, dass der Compiler hier Fehlermeldungen wirft. M\u00f6glicherweise nur ein paar Warnings. Compiliert wird mit <em> -Wall -Wextra -Wconversion<\/em><\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;cassert&gt;\r\n  \r\nbool b_good(bool b) { \r\n  assert(b);\r\n  return true;     \r\n};\r\n\r\nbool b_bad(bool b) { \r\n  assert(b);\r\n  return 2;     \r\n};\r\n\r\nfloat b_bad2(bool b) {\r\n  assert(b);\r\n  return true; \r\n};\r\n\r\nint main() {\r\n  assert(b_good(true)); \/\/ okay\r\n  assert(b_bad(4));     \/\/ bad but works. pass int -&gt;   bool return int -&gt; bool -&gt; bool\r\n  assert(b_bad2(2.0));  \/\/ bad but works. pass float -&gt; bool return bool-&gt; float -&gt; bool\r\n}\r\n<\/pre>\n<p>Der Compiler schweigt und das Programm l\u00e4uft sauber durch. Salopp gesagt, alles was nicht 0 ist, ist wahr. Das funktioniert, aber f\u00f6rdert nicht die Fehlerfreiheit des Programms. <\/p>\n<p>Mit narrowing conversion \/ brace initialization \/ list initialization, mit den geschweiften Klammern gibt es immerhin Warnungen bzw. gleich ein Fehler f\u00fcr Konstanten:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nassert(b_bad({4})); \r\n...\r\nreturn {2};\r\n<\/pre>\n<blockquote><p>error: narrowing conversion of \u20184\u2019 from \u2018int\u2019 to \u2018bool\u2019 inside { } [-Wnarrowing]<br \/>\nassert(b_bad({4}));<\/p>\n<p> error: narrowing conversion of \u20182\u2019 from \u2018int\u2019 to \u2018bool\u2019 inside { } [-Wnarrowing]<br \/>\n   return {2};\n<\/p><\/blockquote>\n<p>Aber \u00fcberall jetzt noch mehr Klammern einbauen kann nicht die L\u00f6sung sein. Beim return kann ich es mir noch vorstellen, aber nicht bei jedem Funktionsparameter.<\/p>\n<p>Wie sieht es aus wenn statt dem Typ bool das Konzept Boolean genutzt wird? Nun, meine Variante dieses Konzept ist nicht wirklich eins, es gleicht mehr einem constraint. Ein Konzept soll ja auch m\u00f6glichst vielen constrain bestehen. Aber es funktioniert. Ersetzt man die Funktionsparameter Typen durch das Konzept Boolean, kommen gute Fehlermeldungen, wie gew\u00fcnscht.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\ntemplate &lt;class B&gt; concept bool Boolean = std::is_same&lt;B, bool&gt;::value;\r\n<\/pre>\n<blockquote><p>error: cannot call function \u2018auto b_bad(auto:2) [with auto:2 = int]\u2019<br \/>\n note:   constraints not satisfied<br \/>\n Boolean b_bad(Boolean b) {<br \/>\n note: within \u2018template<class B> concept const bool Boolean<B> [with B = int]\u2019<br \/>\n template <class B> concept bool  Boolean =  std::is_same<B, bool>::value;<br \/>\n <strong>\u2018std::is_same<int, bool>::value\u2019 evaluated to false<\/strong><\/p>\n<p>error: cannot call function \u2018float b_bad2(auto:3) [with auto:3 = double]\u2019<br \/>\n note:   constraints not satisfied<br \/>\n float b_bad2(Boolean b) {<br \/>\n  note: within \u2018template<class B> concept const bool Boolean<B> [with B = double]\u2019<br \/>\n template <class B> concept bool  Boolean =  std::is_same<B, bool>::value;<br \/>\n <strong>note: \u2018std::is_same<double, bool>::value\u2019 evaluated to false<\/strong>\n<\/p><\/blockquote>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;type_traits&gt;\r\n\r\n\/\/ Forcing a concept to be a fixed type. Is this a good concept or misuse?\r\ntemplate&lt;typename B&gt;\r\nconcept bool BoolType = std::is_same&lt;B,bool&gt;::value;\r\n\r\ntemplate&lt; typename ObjectType&gt;\r\nconcept bool ObjectInterface = requires (ObjectType obj, int a) {\r\n  \/\/ Compound Requirements\r\n\r\n  \/\/ does not work. \r\n  \/\/ 4) trailing-return-type that names a type that does not use placeholders: \r\n  \/\/ 4a) the type named by trailing-return-type is valid (type constraint)\r\n  \/\/ 4b) the result of the expression is implicitly convertible to that type (implicit conversion constraint)\r\n  \/\/ {obj.u1(a)} -&gt; bool;  \r\n  \r\n  \/\/ works\r\n  \/\/ 3) trailing-return-type that names a type that uses placeholders,\r\n  \/\/ the type must be deducible from the type of the expression (argument deduction constraint)  \r\n  {obj.u1(a)} -&gt; BoolType;\r\n};\r\n\r\nvoid f(ObjectInterface obj) {\r\n  obj.u1(1);\r\n}\r\n\r\nstruct MyType {\r\n  auto u1(int a) { return false; }\r\n\/\/  auto u1(int a) { return 1; } \/\/ error\r\n};\r\n\r\nint main () {\r\n  f(MyType());\r\n}\r\n<\/pre>\n<blockquote><p>\nconcept.cpp: In function \u2018int main()\u2019:<br \/>\nconcept.cpp:30:13: error: cannot call function \u2018void f(auto:1) [with auto:1 = MyType]\u2019<br \/>\n   f(MyType());<br \/>\n             ^<br \/>\nconcept.cpp:21:6: note:   constraints not satisfied<br \/>\n void f(ObjectInterface obj) {<br \/>\n      ^<br \/>\nconcept.cpp:8:14: note: within \u2018template<class ObjectType> concept const bool ObjectInterface<ObjectType> [with ObjectType = MyType]\u2019<br \/>\n concept bool ObjectInterface = requires (ObjectType obj, int a) {<br \/>\n              ^~~~~~~~~~~~~~~<br \/>\nconcept.cpp:8:14: note:     with \u2018MyType obj\u2019<br \/>\nconcept.cpp:8:14: note:     with \u2018int a\u2019<br \/>\nconcept.cpp:8:14: note: <strong>unable to deduce placeholder type \u2018BoolType\u2019 from \u2018obj.MyType::u1(a)\u2019<\/strong>\n<\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Das Problem von impliziten Konversionen in C\/C++ ist nicht neu. Wenn integer in floats und floats in integer kopiert werden und dabei die Werte der Zahlen ver\u00e4ndert werden, kann das in wissenschaftlichen Anwendungen katastrophale Auswirkungen haben. Es m\u00fcssen verschiedene Szenarien beachtet werden. So ist z.b. das kopieren eines 32bit integer in ein 64bit float kein [&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-3379","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\/3379","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=3379"}],"version-history":[{"count":9,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3379\/revisions"}],"predecessor-version":[{"id":3388,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3379\/revisions\/3388"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3379"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3379"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3379"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}