"use strict";(self.webpackChunkfi=self.webpackChunkfi||[]).push([[7084],{53181:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>a,toc:()=>l});var i=t(85893),s=t(11151);const o={slug:"placeholders",title:"Placeholders",description:"Placeholders that are quite convenient to use when working on the code.\n",last_update:{date:new Date("2023-11-24T00:00:00.000Z")}},r=void 0,a={id:"exceptions-and-raii/2023-11-24-placeholders",title:"Placeholders",description:"Placeholders that are quite convenient to use when working on the code.\n",source:"@site/cpp/07-exceptions-and-raii/2023-11-24-placeholders.md",sourceDirName:"07-exceptions-and-raii",slug:"/exceptions-and-raii/placeholders",permalink:"/cpp/exceptions-and-raii/placeholders",draft:!1,unlisted:!1,editUrl:"https://github.com/mfocko/blog/tree/main/cpp/07-exceptions-and-raii/2023-11-24-placeholders.md",tags:[],version:"current",lastUpdatedAt:1700784e3,formattedLastUpdatedAt:"Nov 24, 2023",frontMatter:{slug:"placeholders",title:"Placeholders",description:"Placeholders that are quite convenient to use when working on the code.\n",last_update:{date:"2023-11-24T00:00:00.000Z"}},sidebar:"autogeneratedBar",previous:{title:"Exceptions and RAII",permalink:"/cpp/category/exceptions-and-raii"},next:{title:"Environment",permalink:"/cpp/environment"}},c={},l=[{value:"Design",id:"design",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Wrapping in a function",id:"wrapping-in-a-function",level:2},{value:"Magic trick",id:"magic-trick",level:2},{value:"Finishing off with 2 more exceptions",id:"finishing-off-with-2-more-exceptions",level:2},{value:"Post-mortem",id:"post-mortem",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.a)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)(n.p,{children:["Here we will try to implement some placeholders that you can find in other\nlanguages, but I miss them in the C++. I'm taking the inspiration from languages\nlike Rust (all that we will implement) or Kotlin (",(0,i.jsx)(n.code,{children:"TODO"}),") that have them\nimplemented."]}),"\n",(0,i.jsx)(n.p,{children:"You may ask what placeholders do we need in the code, in our case we will be\ntalking about TODOs and unexpected situations, such as not implemented branches."}),"\n",(0,i.jsx)(n.p,{children:"Namely we will implement"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"todo"}),","]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"unimplemented"}),", and"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"unreachable"}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"design",children:"Design"}),"\n",(0,i.jsx)(n.p,{children:"If we take the two languages mentioned above as examples, there are at least two\nways how to implement them:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.em,{children:"panic"})," when they are reached (as they do in Rust), or"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.em,{children:"raise"})," an exception when they are reached (as they do in Kotlin)."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["I will choose raising an exception, since the closest equivalent of ",(0,i.jsx)(n.em,{children:"panic"})," in\nC++ would be ",(0,i.jsx)(n.code,{children:"assert"}),"s that are (by default) disabled in the ",(0,i.jsx)(n.em,{children:"release builds"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"However I am too lazy to do:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'throw todo();\n// or\nthrow todo("optional note");\n'})}),"\n",(0,i.jsx)(n.p,{children:"Therefore we will implement exceptions and also wrap them in functions, so that\nwe can do:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'todo();\n// or\ntodo("optional note");\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsx)(n.p,{children:"Wrapping them in a function (or macro) will allow us to do a little magic trick."})}),"\n",(0,i.jsx)(n.h2,{id:"implementation",children:"Implementation"}),"\n",(0,i.jsxs)(n.p,{children:["We're going to utilize the exceptions, so we'll need to include the ",(0,i.jsx)(n.code,{children:"exception"}),"\nheader and we will start with a simple ",(0,i.jsx)(n.code,{children:"_todo"})," exception class."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n\nclass _todo : public std::exception {\n std::string cause;\n\n public:\n _todo() : cause("not yet implemented") {}\n _todo(std::string&& excuse) : cause("not yet implemented: " + excuse) {}\n virtual const char* what() const throw() { return cause.c_str(); }\n};\n'})}),"\n",(0,i.jsx)(n.p,{children:"In this case we have 2 constructors:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["default constructor without any parameters that will return just\n",(0,i.jsx)(n.code,{children:"not yet implemented"})]}),"\n",(0,i.jsxs)(n.li,{children:["and one parametrized with an \u201cexcuse\u201d that will return string like:\n",(0,i.jsx)(n.code,{children:"not yet implemented: \u2039excuse\u203a"})]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"If we were to use it now, we would need to do something like:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include "placeholders.hpp"\n\nint main() {\n throw _todo();\n return 0;\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"wrapping-in-a-function",children:"Wrapping in a function"}),"\n",(0,i.jsx)(n.p,{children:"I am a lazy person, so we will wrap the exception in a function that will throw\nit:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"void todo() {\n throw _todo();\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"This can be used like:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include "placeholders.hpp"\n\nint main() {\n todo();\n return 0;\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"magic-trick",children:"Magic trick"}),"\n",(0,i.jsxs)(n.p,{children:["At the beginning I've mentioned that by wrapping the exceptions in a helper\nfunctions that will throw them, we can do a nice magic trick ","\ud83d\ude04"," This trick\nwill consist of formatted string and for that we will use\n",(0,i.jsx)(n.a,{href:"https://en.cppreference.com/w/cpp/utility/format/format",children:(0,i.jsx)(n.code,{children:"std::format"})})," that is\navailable since C++20."]}),"\n",(0,i.jsxs)(n.p,{children:["We just need to add one more overload for our ",(0,i.jsx)(n.code,{children:"todo()"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"#include \n\ntemplate< class... Args >\nvoid todo(std::format_string fmt, Args&&... args) {\n throw _todo(std::format(fmt, args...));\n}\n"})}),"\n",(0,i.jsx)(n.h2,{id:"finishing-off-with-2-more-exceptions",children:"Finishing off with 2 more exceptions"}),"\n",(0,i.jsx)(n.p,{children:"Now we can repeat the same process for the other two exceptions I've mentioned"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"unimplemented"}),", and"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"unreachable"}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"In the end we should end up with something like this:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:'#include \n#include \n#include \n\nclass _todo : public std::exception {\n std::string cause;\n\n public:\n _todo() : cause("not yet implemented") {}\n _todo(std::string&& excuse) : cause("not yet implemented: " + excuse) {}\n virtual const char* what() const throw() { return cause.c_str(); }\n};\n\nvoid todo() { throw _todo(); }\n\ntemplate \nvoid todo(std::format_string fmt, Args&&... args) {\n throw _todo(std::format(fmt, args...));\n}\n\nclass _unimplemented : public std::exception {\n std::string cause;\n\n public:\n _unimplemented() : cause("not implemented") {}\n _unimplemented(std::string&& excuse)\n : cause("not implemented: " + excuse) {}\n virtual const char* what() const throw() { return cause.c_str(); }\n};\n\nvoid unimplemented() { throw _unimplemented(); }\n\ntemplate \nvoid unimplemented(std::format_string fmt, Args&&... args) {\n throw _unimplemented(std::format(fmt, args...));\n}\n\nclass _unreachable : public std::exception {\n std::string cause;\n\n public:\n _unreachable() : cause("entered unreachable code") {}\n _unreachable(std::string&& excuse)\n : cause("entered unreachable code: " + excuse) {}\n virtual const char* what() const throw() { return cause.c_str(); }\n};\n\nvoid unreachable() { throw _unreachable(); }\n\ntemplate \nvoid unreachable(std::format_string fmt, Args&&... args) {\n throw _unreachable(std::format(fmt, args...));\n}\n'})}),"\n",(0,i.jsx)(n.admonition,{type:"info",children:(0,i.jsxs)(n.p,{children:["Final source code: ",(0,i.jsx)(n.a,{href:"pathname:///files/cpp/exceptions-and-raii/placeholders/placeholders.hpp",children:(0,i.jsx)(n.code,{children:"placeholders.hpp"})})]})}),"\n",(0,i.jsx)(n.h2,{id:"post-mortem",children:"Post-mortem"}),"\n",(0,i.jsxs)(n.p,{children:["One of the things, I've forgotten about, is the fact that static analysis of\nyour code has no way to know those helper functions we've created as shortcuts\ndon't return and just throw the exception right away. Therefore we need to mark\nthem with ",(0,i.jsx)(n.code,{children:"[[noreturn]]"})," to let the static analysis know that we ",(0,i.jsx)(n.strong,{children:"never"}),"\nreturn from such functions. For example:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-cpp",children:"[[noreturn]] void unreachable() { throw _unreachable(); }\n\ntemplate \n[[noreturn]] void unreachable(std::format_string fmt, Args&&... args) {\n throw _unreachable(std::format(fmt, args...));\n}\n"})})]})}function h(e={}){const{wrapper:n}={...(0,s.a)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},11151:(e,n,t)=>{t.d(n,{Z:()=>a,a:()=>r});var i=t(67294);const s={},o=i.createContext(s);function r(e){const n=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]);