{"id":39,"date":"2009-03-28T10:39:09","date_gmt":"2009-03-28T17:39:09","guid":{"rendered":"http:\/\/gameangst.com\/?p=39"},"modified":"2009-03-28T10:39:43","modified_gmt":"2009-03-28T17:39:43","slug":"how-not-to-c","status":"publish","type":"post","link":"http:\/\/gameangst.com\/?p=39","title":{"rendered":"How not to C++"},"content":{"rendered":"<p>One of the things I love about C++ is the fact that it is a strongly-typed language. \u00a0An essential element of good programming is writing code that is self-documenting. \u00a0The more explicit you are in your code, the less chance that you, your fellow programmers, or even the compiler will misunderstand your purpose. \u00a0That said, strong typing can be taken too far.<\/p>\n<p>One of the problems I noted in the first math library I worked with was excessive repetition of common operations. \u00a0Vector normalization particularly seemed to be done everywhere. \u00a0The library had numerous functions that took as an input parameter a 3 component vector and required that vector to be normalized. \u00a0The principle consideration in the design of the library must have been ease of use, because most library functions accepted unnormalized inputs and always performed a normalization step themselves. \u00a0The unfortunate downside of this approach was that vectors were frequently being renormalized unnecessarily and performance suffered.<\/p>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\"><span class=\"kw4\">struct<\/span> Vector3 <span class=\"br0\">&#123;<\/span> &#8230; <span class=\"br0\">&#125;<\/span>;<br \/>\n<span class=\"kw4\">struct<\/span> Plane <span class=\"br0\">&#123;<\/span> &#8230; <span class=\"br0\">&#125;<\/span>;<\/p>\n<p><span class=\"co1\">\/\/ Construct a Plane object given a point on the plane and the plane&#8217;s normal<\/span><br \/>\nPlane CreatePlane<span class=\"br0\">&#40;<\/span>Vector3 point, Vector3 normal<span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; normal = Normalize<span class=\"br0\">&#40;<\/span>normal<span class=\"br0\">&#41;<\/span>;<br \/>\n&nbsp; &nbsp; &#8230;<br \/>\n<span class=\"br0\">&#125;<\/span><\/div>\n<\/blockquote>\n<p>Years later when I had an opportunity to design my own math library, I attempted to solve this problem with C++ types. \u00a0I introduced a new type in my math library called NormalizedVector3. \u00a0Functions like CreatePlane were modified to take NormalizedVector3 objects and consequently they no longer needed to perform possibly redundant normalizations. \u00a0My first implementation of NormalizedVector3 looked something like this:<\/p>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\"><span class=\"kw4\">struct<\/span> NormalizedVector3<br \/>\n<span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ construction from Vector3 must normalize!<\/span><br \/>\n&nbsp; &nbsp; NormalizedVector3<span class=\"br0\">&#40;<\/span>Vector3 v<span class=\"br0\">&#41;<\/span>;<\/p>\n<p>&nbsp; &nbsp; <span class=\"co1\">\/\/ construction from scalars must normalize!<\/span><br \/>\n&nbsp; &nbsp; NormalizedVector3<span class=\"br0\">&#40;<\/span><span class=\"kw4\">float<\/span> x, <span class=\"kw4\">float<\/span> y, <span class=\"kw4\">float<\/span> z<span class=\"br0\">&#41;<\/span>;<\/p>\n<p>&nbsp; &nbsp; <span class=\"co1\">\/\/ cast operator allows NormalizedVector3 to be compatible with Vector3<\/span><br \/>\n&nbsp; &nbsp; operator Vector3<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw4\">const<\/span>;<\/p>\n<p>&nbsp; &nbsp; <span class=\"co1\">\/\/ can&#8217;t allow non-const access to members<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw4\">float<\/span> X<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw4\">const<\/span>;<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">float<\/span> Y<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw4\">const<\/span>;<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">float<\/span> Z<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw4\">const<\/span>;<\/p>\n<p><span class=\"kw2\">private<\/span>:<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">float<\/span> x, y, z;<br \/>\n<span class=\"br0\">&#125;<\/span>;<\/div>\n<\/blockquote>\n<p>This worked reasonably well but there were a few problems. \u00a0NormalizedVector3 allowed implicit construction from Vector3 and implicit casting to Vector3 so it could take advantage of all the built-in functionality of a Vector3. \u00a0Unfortunately this also meant it introduced a lot of opportunities for implicit normalization.<\/p>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\">NormalizedVector3 nv0, nv1;<br \/>\nNormalizedVector3 nv2 = nv0 * nv1; &nbsp; &nbsp; <span class=\"co1\">\/\/ implicit conversion to Vector3 and<\/span><br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class=\"co1\">\/\/ back to NormalizedVector3<\/span><\/p>\n<p>Vector3 point, normal;<br \/>\nPlane p = CreatePlane<span class=\"br0\">&#40;<\/span>point, normal<span class=\"br0\">&#41;<\/span>; <span class=\"co1\">\/\/ implicit conversion to NormalizedVector3<\/span><\/div>\n<\/blockquote>\n<p>Since operations like vector multiplication are not length-preserving, they are handled by implicit conversions to and from Vector3. \u00a0Not only is this confusing, it is exactly the sort of hidden (and likely unnecessary) cost I was trying to avoid. \u00a0My second implementation made the constructor of NormalizedVector3 explicit, which added a bit more overhead to the API but also helped highlight what kind of work was going on underneath the hood.<\/p>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\">NormalizedVector3 nv0, nv1;<br \/>\nNormalizedVector3 nv2 = NormalizedVector3<span class=\"br0\">&#40;<\/span> nv0 * nv1 <span class=\"br0\">&#41;<\/span>;<\/p>\n<p>Vector3 point, normal;<br \/>\nPlane p = CreatePlane<span class=\"br0\">&#40;<\/span>point, NormalizedVector3<span class=\"br0\">&#40;<\/span>normal<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#41;<\/span>;<\/div>\n<\/blockquote>\n<p>Of couse as soon all the hidden normalizations were brought to light, I discovered a host of undesirable ones. \u00a0The math library included functions to multiply vectors and matrices, and most of the time the matrices represented orthogonal transformations. \u00a0Now all of a sudden transforming a NormalizedVector3 by a matrix required renormalization!<\/p>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\">NormalizedVector vOld;<br \/>\nMatrix3x3 orthogonalTransform;<br \/>\nNormalizedVector3 vNew =<br \/>\n&nbsp; &nbsp; NormalizedVector3<span class=\"br0\">&#40;<\/span> orthogonalTransform * vOld <span class=\"br0\">&#41;<\/span>; <span class=\"co1\">\/\/ unnecessary normalization<\/span><\/div>\n<\/blockquote>\n<p>Luckily I already had a solution to this problem, more C++ types! \u00a0I created an OrthonormalMatrix3x3 class with a relationship to Matrix3x3 much like NormalizedVector3&#8217;s relationship to Vector3. \u00a0I was then able to provide a function for transforming NormalizedVector3s by OrthonormalMatrix3x3s that returned NormalizedVector3s and, as if by magic, all concerns about unnecessary normalization disappeared! \u00a0Okay, not really. \u00a0What actually happened was I discovered lots of operations on orthonormal matrices that preserved orthonomality but were now resulting in unnecessary re-orthonormalizations. \u00a0I attempted to provide overloads of those operations to remove the unnecessary conversions between Matrix3x3s and OrthonormalMatrix3x3s and hilarity ensued.<\/p>\n<p>By this time it was pretty clear I was halfway to Wonderland and it was time to turn around and crawl back out of the rabbit hole. \u00a0I deleted all references to OrthonormalMatrix3x3 and NormalizedVector3, which by this point constituted an unhealthy percentage of my math library&#8217;s code, and made just two modifications to the original design:<\/p>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\"><span class=\"co1\">\/\/ Construct a Plane object given a point on the plane and the plane&#8217;s normal<\/span><br \/>\n<span class=\"co1\">\/\/ The normal vector must be unit length.<\/span><br \/>\nPlane CreatePlane<span class=\"br0\">&#40;<\/span>Vector3 origin, Vector3 normal<span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw3\">assert<\/span><span class=\"br0\">&#40;<\/span> IsUnitLength<span class=\"br0\">&#40;<\/span>normal<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#41;<\/span>;<br \/>\n&nbsp; &nbsp; &#8230;<br \/>\n<span class=\"br0\">&#125;<\/span><\/div>\n<\/blockquote>\n<p>The lesson here is that simplicity is every bit as important in API design as correctness. \u00a0You can try to make your API so clever that even an idiot can&#8217;t misuse it, or you can try to make your API so simple that even an idiot can&#8217;t misunderstand it. \u00a0The result is usually the same&#8211;high performance, error-free code&#8211;but your clients will be happier and your codebase will be much, much leaner.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the things I love about C++ is the fact that it is a strongly-typed language. \u00a0An essential element of good programming is writing code that is self-documenting. \u00a0The more explicit you are in your code, the less chance that you, your fellow programmers, or even the compiler will misunderstand your purpose. \u00a0That said, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[8],"_links":{"self":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/39"}],"collection":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=39"}],"version-history":[{"count":18,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/39\/revisions"}],"predecessor-version":[{"id":44,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/39\/revisions\/44"}],"wp:attachment":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=39"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=39"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=39"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}