{"id":9577,"date":"2020-08-27T09:17:05","date_gmt":"2020-08-27T07:17:05","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=9577"},"modified":"2023-10-23T10:43:14","modified_gmt":"2023-10-23T08:43:14","slug":"implementing-a-state-machine-in-c17-part-3-compile-time-strings","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/en\/implementing-a-state-machine-in-c17-part-3-compile-time-strings\/","title":{"rendered":"Implementing a State Machine in C++17 \u2013 part 3 \u2013 compile time strings"},"content":{"rendered":"\n<p>In the previous series of articles (<a href=\"https:\/\/sii.pl\/blog\/implementing-a-state-machine-in-c17\/\">part1<\/a>, <a href=\"https:\/\/sii.pl\/blog\/implementing-a-state-machine-in-c17-part-2\/\">part2<\/a>) we&#8217;ve played with a simple state machine with some basic functionality. As the machine grows bigger, the need for debugging utilities becomes more important. To implement such utilities we need to do some groundwork first. All state, event, and action names are known at compile-time and for most of them, we could easily generate a string with the name during compilation. What&#8217;s problematic is that some of the types could be parametrized and would require some form of compile-time string concatenation. That&#8217;s exactly what we are going to explore in this article \ud83d\ude42<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Basics<\/h2>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/string.jpg\"><img decoding=\"async\" width=\"243\" height=\"126\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/string.jpg\" alt=\"\" class=\"wp-image-9590\"\/><\/a><\/figure>\n\n\n\n<p>As most of you probably know, string literals in C++ are of type <strong><code>const char[N]<\/code><\/strong> and what&#8217;s more important they always include the terminating null character. Another important fact is that string literals are known at compile-time, which means we can do something like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nconstexpr char&#x5B;] value = \"Foo\";\n<\/pre><\/div>\n\n\n<p>For each variable marked with <strong><code>constexpr<\/code><\/strong> the compiler enforces that it&#8217;s value must be calculated during compilation (C++20 expands this idea with <strong><code>consteval<\/code><\/strong> and <strong><code>constinit<\/code><\/strong> keywords, but that&#8217;s a story for another article). For functions there&#8217;s a slight difference. There are some restrictions placed on <strong><code>constexpr<\/code><\/strong> functions so that their return value could be used as a compile time constant, but what&#8217;s important is that the compiler will still generate an &#8216;ordinary&#8217; (non-constexpr) function that would be used when non-constexpr arguments are passed.<\/p>\n\n\n\n<p>So, getting back to string literals. Unfortunately, there are no easy ways to return a native array from a function. What&#8217;s even more disappointing is that extra care needs to be taken when passing arrays to functions. Take a look at this example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid trap(const char arr&#x5B;7])\n{\nstd::cout &lt;&lt; sizeof(arr) &lt;&lt; std::endl;\n}\n<\/pre><\/div>\n\n\n<p>Since array arguments automatically decay to pointers this code is equivalent to:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid trap(const char* arr)\n{\nstd::cout &lt;&lt; sizeof(arr) &lt;&lt; std::endl;\n}\n<\/pre><\/div>\n\n\n<p>Looks different. Doesn&#8217;t it? \ud83d\ude42 What&#8217;s even more annoying we cannot use template parameter deduction to &#8216;extract&#8217; the size of the array<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;typename T, std::size_t N&gt;\nvoid trap(T arr&#x5B;N]) \/\/ &lt;-- This doesn&#039;t work\n{\nstd::cout &lt;&lt; (sizeof(T) * N) &lt;&lt; std::endl;\n}\n<\/pre><\/div>\n\n\n<p>Fortunately there&#8217;s a neat little C++ trick that we can use here. Even though arrays automatically decay to pointers, references to arrays do not follow this rule&#8230;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;typename T, std::size_t N&gt;\nvoid trap(T (&amp;arr)&#x5B;N]) \/\/ &lt;-- Works!\n{\nstd::cout &lt;&lt; sizeof(arr) &lt;&lt; std::endl; \/\/ &lt;-- This also works\n}\n<\/pre><\/div>\n\n\n<p>Syntax looks a bit odd due to the parenthesis, but at least it works \ud83d\ude42 . Since storing a reference to an array (which might be temporary value) is not always the best idea, we&#8217;ll use <strong><code>std::array<\/code><\/strong> to hold the values. It&#8217;s just a thin wrapper around a regular array and most of its methods are already <strong><code>constexpr<\/code><\/strong>. A bit of required functionality is missing though, so we&#8217;ll need to implement it ourselves.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Extending std::array<\/h2>\n\n\n\n<p>As we discussed before, a string literal is just an array of characters, and since we&#8217;ll be working mostly with <strong><code>std::array<\/code><\/strong> we need to have a mechanism to convert one to another. Let&#8217;s start with the conversion from a regular array. <strong><code>std::array<\/code><\/strong> uses aggregate initialisation so we cannot simply pass an array as a constructor argument. So we&#8217;re on our own here. There are of course multiple ways to do implement such a function. Let&#8217;s start with something based on index sequences and parameter packs.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;typename T, std::size_t N&gt;\nconstexpr std::array&lt;T, N&gt; toStdArray(T (&amp;arr)&#x5B;N])\n{\nreturn toStdArray(arr, std::make_index_sequence&lt;N&gt;());\n}\n<\/pre><\/div>\n\n\n<p>The rough idea is quite straightforward. We start with capturing the array by reference, then we create an index sequence from 0 to (N-1) and pass it to a template with a parameter pack<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;typename T, std::size_t N, std::size_t... Idx&gt;\nconstexpr std::array&lt;T, N&gt; toStdArray(T (&amp;arr)&#x5B;N], std::index_sequence&lt;Idx...&gt;)\n{\nreturn {arr&#x5B;Idx]...};\n}\n<\/pre><\/div>\n\n\n<p>Here we just need to expand the parameter pack containing the indexes and we&#8217;re ready to construct the array. Note that <strong><code>std::index_sequence<\/code><\/strong> is only used to deduce a parameter pack containing all the numbers 0 to N-1.<br>Ok, we know how to construct a <strong><code>std::array<\/code><\/strong> out of a regular array. Now we need to implement a function that combines two arrays together.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;typename T, std::size_t LeftSize, std::size_t RightSize, std::size_t... LeftIdx, std::size_t... RightIdx&gt;\nconstexpr std::array join(const std::array&amp; lhs,\nconst std::array&amp; rhs,\nstd::index_sequence&lt;LeftIdx...&gt;,\nstd::index_sequence&lt;RightIdx...&gt;)\n{\nreturn {lhs&#x5B;LeftIdx]..., rhs&#x5B;RightIdx]...};\n}\n \ntemplate &lt;typename T, std::size_t LeftSize, std::size_t RightSize&gt;\nconstexpr std::array&lt;T, LeftSize + RightSize&gt; join(const std::array&lt;T, LeftSize&gt;&amp; lhs,\nconst std::array&lt;T, RightSize&gt;&amp; rhs)\n{\nreturn join(lhs,\nrhs,\nstd::make_index_sequence&lt;LeftSize&gt;(),\nstd::make_index_sequence&lt;RightSize&gt;());\n}\n<\/pre><\/div>\n\n\n<p>That&#8217;s the same idea but with a bit more template parameters \ud83d\ude42 .<\/p>\n\n\n\n<p>There&#8217;s one slight detail still missing. Remember that all string literals end with a null value at the end and we don&#8217;t want that value to end up in the middle of a newly joined string. We need a mechanism to trim or in more general terms, resize the array.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;std::size_t NewSize, typename T, std::size_t OldSize, std::size_t... Indexes&gt;\nconstexpr std::array&lt;T, NewSize&gt; resize(const std::array&lt;T, OldSize&gt;&amp; arr, std::index_sequence&lt;Indexes...&gt;)\n{\nreturn {arr&#x5B;Indexes]...};\n}\n \ntemplate &lt;std::size_t NewSize, typename T, std::size_t OldSize&gt;\nconstexpr std::array&lt;T, NewSize&gt; resize(const std::array&lt;T, OldSize&gt;&amp; arr)\n{\nconstexpr std::size_t minSize = std::min(OldSize, NewSize);\nreturn resize(arr, std::make_index_sequence());\n}\n<\/pre><\/div>\n\n\n<p>Biggest difference here is that we can specify the result array size, so we could create a new, larger array and use the previous one as initial values.<\/p>\n\n\n\n<p>As a last utility function we&#8217;ll implement <code>areEqual<\/code>. Even though <strong><code>std::array<\/code><\/strong> provides an equality operator it&#8217;s not marked as constexpr (but will be in C++20) and we can&#8217;t use it. We&#8217;ll once again use the <strong><code>std::index_sequence<\/code><\/strong> trick, but this time we&#8217;ll also use a fold expresion (introduced in C++17) to generate a big boolean expression comparing all the elements.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;typename T, std::size_t N, std::size_t... Idx&gt;\nconstexpr bool areEqual(const std::array&lt;T, N&gt;&amp; lhs, const std::array&lt;T, N&gt;&amp; rhs, std::index_sequence&lt;Idx...&gt;)\n{\nreturn ((lhs&#x5B;Idx] == rhs&#x5B;Idx]) &amp;&amp; ...);\n}\n \ntemplate &lt;typename T, std::size_t N&gt;\nconstexpr bool areEqual(const std::array&lt;T, N&gt;&amp; lhs, const std::array&lt;T, N&gt;&amp; rhs)\n{\nreturn areEqual(lhs, rhs, std::make_index_sequence());\n}\n<\/pre><\/div>\n\n\n<p>The fold expression will expand to something like: <code>(lhs[0] == rhs[0]) &amp;&amp; (lhs[1] == rhs[1]) &amp;&amp; ... &amp;&amp; (lhs[N-1] == rhs[N-1])<\/code> which has this nice property that it will stop at first difference due to short circuit evaluation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Compile time tests<\/h2>\n\n\n\n<p><strong><code>constexpr<\/code><\/strong> modifier on functions allows those functions to be used in static assertions. What we can achieve with it is that we can create simple compile-time tests that could be included in the header. When we make a mistake or a typo the compiler would see a failed <strong><code>static_assert<\/code><\/strong> and stop the compilation with an error. In other words, the code compiles only when all the tests pass, which is pretty neat. On the other hand, we don&#8217;t want to include test-only code in the resulting binary, so we need to add some special measures to circumvent it.<br>First of all, it would be a good idea to wrap the code in an anonymous namespace. This causes all the functions in that namespace to have an internal linkage so most compilers would be able to remove those functions if they&#8217;re not called in the same file. Unfortunately, this also leads to a warning. There&#8217;s an easy fix for that. We could mark those functions with a <strong><code>maybe_unused<\/code><\/strong> attribute.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace tests\n{\nnamespace\n{\n \n&#x5B;&#x5B;maybe_unused]] constexpr void testToStdArray()\n{\nconstexpr int input&#x5B;] = {1, 2, 3};\nconstexpr auto output = toStdArray(input);\nconstexpr std::array expected = {1, 2, 3};\nstatic_assert(areEqual(expected, output));\n}\n \n&#x5B;&#x5B;maybe_unused]] constexpr void testJoin()\n{\nconstexpr std::array inputA = {1, 2, 3};\nconstexpr std::array inputB = {4, 5};\nconstexpr std::array expected = {1, 2, 3, 4, 5};\nstatic_assert(areEqual(expected, join(inputA, inputB)));\n}\n \n&#x5B;&#x5B;maybe_unused]] constexpr void testResize()\n{\nconstexpr std::array input = {1, 2, 3};\nconstexpr std::array expectedShorter = {1, 2};\nconstexpr std::array expectedLonger = {1, 2, 3, 0};\nstatic_assert(areEqual(expectedShorter, resize(input)));\nstatic_assert(areEqual(expectedLonger, resize(input)));\n}\n \n}\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Creating <strong><code>StaticString<\/code><\/strong> class<\/h2>\n\n\n\n<p>Now we have all the necessary tools to create <strong><code>StaticString<\/code><\/strong> class. The class itself needs to be a template parametrized by string length, but hopefully in most cases the compiler will be able to deduce the exact value for us. The most convenient way to create a <strong><code>StaticString<\/code><\/strong> instance would be to create it from a string literal and since we plan to store the data in a <strong><code>std::array<\/code><\/strong> let&#8217;s add another constructor for that case.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;std::size_t N&gt;\nclass StaticString\n{\npublic:\nconstexpr StaticString(const char (&amp;chars)&#x5B;N])\n: chars(toStdArray(chars))\n{\n}\n \nconstexpr StaticString(std::array&lt;const char, N&gt; chars)\n: chars(std::move(chars))\n{\n}\n \nprivate:\nstd::array&lt;const char, N&gt; chars;\n};\n<\/pre><\/div>\n\n\n<p>Now it&#8217;s time to implement the main functionality &#8211; ability to add two <strong><code>StaticStrings<\/code><\/strong> together. The operation itself is pretty straightforward with two minor details. First of all, we need to consider the null character when joining the strings, but that can be circumvented by simply shrinking the first string by 1 character. The second issue is a bit more subtle. <strong><code>StaticStrings<\/code><\/strong> of different lengths are considered different types and thus cannot access each other&#8217;s private members. Fortunately, there&#8217;s a way to fix it without exposing the data to unrelated classes. Friend specification can be introduced as a template, meaning we can give access to a range of types with a single statement.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/concatenation.jpg\"><img decoding=\"async\" width=\"477\" height=\"281\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/concatenation.jpg\" alt=\"\" class=\"wp-image-9591\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/concatenation.jpg 477w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/concatenation-300x177.jpg 300w\" sizes=\"(max-width: 477px) 100vw, 477px\" \/><\/a><\/figure>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate &lt;std::size_t M&gt;\nconstexpr StaticString&lt;N + M - 1&gt; operator+(const StaticString&lt;M&gt; &amp;rhs) const\n{\nreturn join(resize&lt;N-1&gt;(chars), rhs.chars);\n}\n \ntemplate &lt;std::size_t M&gt;\nfriend class StaticString;\n<\/pre><\/div>\n\n\n<p>Since we&#8217;d like to test our implementation, let&#8217;s also add an equality operator.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nconstexpr bool operator==(const StaticString&lt;N&gt; &amp;rhs) const\n{\nreturn areEqual(chars, rhs.chars);\n}\n<\/pre><\/div>\n\n\n<p>As a bonus we can provide a getter for data pointer for easier printing later on.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nconstexpr const char* data() const\n{\nreturn chars.data();\n}\n<\/pre><\/div>\n\n\n<p>Finally we can add tests for joining strings to see if they work as planned<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nnamespace tests\n{\nnamespace\n{\n \n&#x5B;&#x5B;maybe_unused]] constexpr void testAdding()\n{\nconstexpr StaticString lhs{\"abc\"};\nconstexpr StaticString rhs{\"de\"};\nconstexpr StaticString expected{\"abcde\"};\nstatic_assert(expected == lhs + rhs);\n}\n \n}\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Looking at the generated code<\/h2>\n\n\n\n<p>Let&#8217;s create a simple test function and look at the disassembled code<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n__attribute__((noinline)) void test()\n{\nconstexpr StaticString first{&quot;&lt;&quot;};\nconstexpr StaticString second{&quot;hello&quot;};\nconstexpr StaticString third{&quot;&gt;&quot;};\nputs(first + second + third);\n}\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n0000000000401180 &lt;test()&gt;:\n401180:       50                      push   rax\n401181:       48 b8 3c 68 65 6c 6c    movabs rax,0x3e6f6c6c65683c\n401188:       6f 3e 00\n40118b:       48 89 04 24             mov    QWORD PTR &#x5B;rsp],rax\n40118f:       48 89 e7                mov    rdi,rsp\n401192:       e8 b9 fe ff ff          call   401050\n401197:       58                      pop    rax\n401198:       c3                      ret\n401199:       0f 1f 80 00 00 00 00    nop    DWORD PTR &#x5B;rax+0x0]\n<\/pre><\/div>\n\n\n<p>As you can see the compiler got rid of most of the copies and reassignments and leaves us with just the evaluated result passed to <strong><code>puts<\/code><\/strong> function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>In this article, we explored the possibility to join strings at compile time. The feat itself might not be so impressive, but it&#8217;s one of the things needed to generate a transition table at compile time. As always, you can find the code in my Github repository: (<a href=\"https:\/\/github.com\/AdamsPL\/state-machine\/\" rel=\"nofollow\" >link<\/a>)<\/p>\n\n\n\n<p>In the next article, we&#8217;ll play a bit with the type system, trying to extract as much information as possible with a hint of functional programming \ud83d\ude09<\/p>\n\n\n\n<p>Stay tuned!<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;9577&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;5&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 5)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Implementing a State Machine in C++17 \u2013 part 3 \u2013 compile time strings&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 5)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>In the previous series of articles (part1, part2) we&#8217;ve played with a simple state machine with some basic functionality. As &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/en\/implementing-a-state-machine-in-c17-part-3-compile-time-strings\/\">Continued<\/a><\/p>\n","protected":false},"author":193,"featured_media":9658,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1320],"tags":[1395,1402,1375,1401,1400],"class_list":["post-9577","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hard-development","tag-c17-en","tag-constexpr-en","tag-embedded-competency-center-en","tag-parameter-pack-en","tag-templates-en"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/embedded-state-machine-3.png","category_names":["Hard development"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/9577"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/users\/193"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/comments?post=9577"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/9577\/revisions"}],"predecessor-version":[{"id":25123,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/9577\/revisions\/25123"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media\/9658"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media?parent=9577"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/categories?post=9577"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/tags?post=9577"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}