"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}];function d(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",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"})})]})})]})}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)}}}]);