{"id":1771,"date":"2024-10-01T09:24:16","date_gmt":"2024-10-01T15:24:16","guid":{"rendered":"https:\/\/www.elbeno.com\/blog\/?p=1771"},"modified":"2024-10-01T09:29:05","modified_gmt":"2024-10-01T15:29:05","slug":"open-content-at-cppcon-one-api-for-types-and-values","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1771","title":{"rendered":"Open Content at CppCon: One API for Types and Values"},"content":{"rendered":"\n<p>This curiosity was part of my CppCon Open Content talk in 2024, &#8220;Another Grab-bag of C++ Oddments&#8221;. The session highlighted C++ oddities, in the spirit of discovery through play.<\/p>\n\n\n\n<p>For reasons (TM), I want to be able to do this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auto x = VALUE(42);        \/\/ 42\nauto y = VALUE(\"hello\"sv); \/\/ std::string_view{\"hello\"}\nauto z = VALUE(int);       \/\/ std::type_identity&lt;int&gt;{}<\/code><\/pre>\n\n\n\n<p>Of note here:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>x<\/code> uses a value that <em>can<\/em> be used as a template argument<\/li>\n\n\n\n<li><code>y<\/code> uses a value that is <em>not<\/em> admissible as a template argument (<code>std::string_view<\/code> is not a <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/template_parameters\" data-type=\"link\" data-id=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/template_parameters\">structural type<\/a>)<\/li>\n\n\n\n<li><code>z<\/code> uses a type &#8220;as if it were a value&#8221;<\/li>\n\n\n\n<li>for my use cases, the &#8220;values&#8221; here are known at compile time and are almost always literals<\/li>\n<\/ul>\n\n\n\n<p>Some use cases I have in mind are things like logging, where I want to be able to say things like&#8230;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>LOG(\"{} {}\", VALUE(42), VALUE(int));<\/code><\/pre>\n\n\n\n<p>&#8230;and have the logging library do the right thing. (If given a type, we might typically do the old <code>__PRETTY_FUNCTION__ <\/code>trick to recover its name.)<\/p>\n\n\n\n<p>Well, it seems fairly obvious that this is going to have to be a macro. Here&#8217;s the skeleton.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define VALUE(...)\n  &#91;] {\n    if constexpr(\/* detect *\/) {\n      \/\/ __VA_ARGS__ is a type\n    } else {\n      \/\/ __VA_ARGS__ is a value\n    }\n  }()<\/code><\/pre>\n\n\n\n<p>I&#8217;m omitting the backslashes at the end of lines for clarity. And I&#8217;m using a variadic macro to allow it to handle arguments with commas in them (like types which are template specializations, for example).<\/p>\n\n\n\n<p>OK, the first thing we need is a way to determine whether we have a type or a value. The only way I know to do that is with an overload set.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;auto&gt; \nconstexpr auto is_type() -&gt; std::false_type;\n\ntemplate &lt;typename&gt;\nconstexpr auto is_type() -&gt; std::true_type;\n\n\/\/ then use the expression\ndecltype(is_type&lt;__VA_ARGS__&gt;())::value<\/code><\/pre>\n\n\n\n<p>This works for <code>42<\/code> and <code>int<\/code>, but not for a <code>string_view<\/code>, because it&#8217;s a hard compilation error to attempt to pass it as a template argument. So let&#8217;s make a type that we <em>can<\/em> pass.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct from_any {\n  template &lt;typename... Ts&gt;\n  constexpr from_any(Ts const &amp;...) {}\n\n  constexpr operator int() const { return 0; }\n};\n\n\/\/ then use the expression\ndecltype(is_type&lt;from_any(__VA_ARGS__)&gt;())::value;<\/code><\/pre>\n\n\n\n<p><code>from_any<\/code> is a class that is implicitly constructible from any argument(s). <code>from_any<\/code> is a structural type, so we could pass it as a template argument (from C++20 onwards), but giving it an implicit conversion to <code>int<\/code> also means this works pre-C++20. We&#8217;ll just change the overload set to be friendly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;int&gt; \/\/ was auto\nconstexpr auto is_type() -&gt; std::false_type;\n\ntemplate &lt;typename&gt;\nconstexpr auto is_type() -&gt; std::true_type;<\/code><\/pre>\n\n\n\n<p>Wait, how does the expression work? Well, consider the cases:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ this is a value of type from_any\n\/\/ constructed from 42\nfrom_any(42)\n\n\/\/ this is a value of type from_any\n\/\/ constructed from a string_view\nfrom_any(\"hello\"sv)\n\n\/\/ this is a type - it's the type of\n\/\/ a function taking int and returning from_any\nfrom_any(int)<\/code><\/pre>\n\n\n\n<p>(Raised eyebrows and murmurs from the audience.) It&#8217;s not common to see &#8220;bare&#8221; function types like this, but they are well-formed. So this gives us what we need to differentiate types and values (including non-structural values).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;int&gt;\nconstexpr auto is_type() -&gt; std::false_type;\ntemplate &lt;typename&gt;\nconstexpr auto is_type() -&gt; std::true_type;\n\nis_type&lt;from_any(42)&gt;()        \/\/ false_type\nis_type&lt;from_any(\"hello\"sv)&gt;() \/\/ false_type\nis_type&lt;from_any(int)&gt;()       \/\/ true_type<\/code><\/pre>\n\n\n\n<p>Now the detection part of the skeleton is fleshed out.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define VALUE(...)\n  &#91;] {\n    if constexpr(decltype(is_type&lt;from_any(__VA_ARGS__)&gt;())::value) {\n      \/\/ __VA_ARGS__ is a type\n    } else {\n      \/\/ __VA_ARGS__ is a value\n    }\n  }()<\/code><\/pre>\n\n\n\n<p>The next thing to do is to figure out what to put in the first half of the <code>if constexpr<\/code> block.<\/p>\n\n\n\n<p>Although only one side at a time will actually be used, both sides have to be syntactically well-formed for both types and values. So we <em>can&#8217;t<\/em> just assume that we have a type, and pass it to a template. But we <em>can<\/em> use a similar trick to <code>is_type<\/code> to recover the type in the top half and then put it into a <code>type_identity<\/code>. (Strictly if we want this to continue working before C++20, we need to provide our own <code>type_identity<\/code> class template, but that is easily done, so I&#8217;ll omit it here.)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;typename&gt; struct typer;\ntemplate &lt;typename T&gt;\nstruct typer&lt;from_any(T)&gt; {\n  using type = T;\n};\n\ntemplate &lt;int&gt;\nconstexpr auto type_of() -&gt; void;\ntemplate &lt;typename T&gt;\nconstexpr auto type_of()\n  -&gt; typename typer&lt;T&gt;::type;\n\n\/\/ and then\nusing actual_type = \n  decltype(type_of&lt;from_any(__VA_ARGS__)&gt;());<\/code><\/pre>\n\n\n\n<p><code>type_of<\/code> is using the same overloading form that <code>is_type<\/code> uses, and this time we&#8217;re using a <code>typer<\/code> helper to recover the <code>T<\/code> from the function type (<code>from_any(T)<\/code>). If a value went through this machinery, it would just spit out <code>void<\/code>, but still be good syntax-wise.<\/p>\n\n\n\n<p>We&#8217;re more than halfway done now:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define VALUE(...)\n  &#91;] {\n    if constexpr(decltype(is_type&lt;from_any(__VA_ARGS__)&gt;())::value) {\n      using actual_type = decltype(type_of&lt;from_any(__VA_ARGS__)&gt;());\n      return std::type_identity&lt;actual_type&gt;{};\n    } else {\n      \/\/ __VA_ARGS__ is a value\n    }\n  }()<\/code><\/pre>\n\n\n\n<p>Just the other half of the <code>if constexpr<\/code> to go.<\/p>\n\n\n\n<p>At this point in the Open Content session, the fire alarm went off! The session was adjourned and rescheduled for lunchtime, opposite Jason Turner&#8217;s session. So if you left here, and went to see Jason at lunch, I don&#8217;t blame you. But you did miss this next bit, which was perhaps the most funny\/surprising moment of the talk&#8230;<\/p>\n\n\n\n<p>Now we need another construct that is syntactically well-formed when given either a value or a type, but this time we just need it to produce the value.<\/p>\n\n\n\n<p>Consider the following syntax:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ special is a type we'll write\n(__VA_ARGS__) + special{};<\/code><\/pre>\n\n\n\n<p>When <code>__VA_ARGS__<\/code> is a <em>value<\/em>, this is a binary plus expression. When <code>__VA_ARGS__<\/code> is a <em>type<\/em>, this is a C-style cast of a unary plus expression. This construction works for both types and values! (Outbursts of &#8220;WHAT&#8221; from the audience.) All we have to do is define <code>special<\/code> appropriately.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct special {\n  template &lt;typename T&gt;\n  friend constexpr auto operator+(T&amp;&amp; t, special) {\n    return t;\n  }\n\n  friend constexpr auto operator+(special) -&gt; special;\n  template &lt;typename T&gt; constexpr operator T() const;\n};<\/code><\/pre>\n\n\n\n<p>Given the <code>if constexpr<\/code>, we&#8217;re only ever going to exercise this code with a value, so the unary plus interpretation will never actually be used. We don&#8217;t need to <em>define<\/em> the unary <code>operator+<\/code> and the conversion operator &#8211; they only need to be <em>declared<\/em> so that the type interpretation is syntactically well-formed.<\/p>\n\n\n\n<p>(Side note: we could have used <code>operator*<\/code> here too, since it&#8217;s also an overloadable operator with both unary and binary forms. Either plus or multiply works.)<\/p>\n\n\n\n<p>Now we are done.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define VALUE(...)\n  &#91;] {\n    if constexpr(decltype(is_type&lt;from_any(__VA_ARGS__)>())::value) {\n      using actual_type = decltype(type_of&lt;from_any(__VA_ARGS__)>());\n      return std::type_identity&lt;actual_type>{};\n    } else {\n      return (__VA_ARGS__) + special{};\n    }\n  }()<\/code><\/pre>\n\n\n\n<p>This does the job we originally wanted. Is it useful? Maybe. It is fun and educational to write and present at CppCon Open Content? Definitely.<\/p>\n\n\n\n<p>Thanks to everyone who came to my open content. See you at the next conference hallway track. And if you can&#8217;t come to a conference, maybe you can spare a couple of hours a month to go to a meetup. If you don&#8217;t have a local one (or even if you do), you are welcome at the <a href=\"https:\/\/www.meetup.com\/north-denver-metro-c-meetup\/\" data-type=\"link\" data-id=\"https:\/\/www.meetup.com\/north-denver-metro-c-meetup\/\">Denver C++ meetup<\/a>. We meet on-site and remotely on the first Thursday of every month. It&#8217;s always fun.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This curiosity was part of my CppCon Open Content talk in 2024, &#8220;Another Grab-bag of C++ Oddments&#8221;. The session highlighted C++ oddities, in the spirit of discovery through play. For reasons (TM), I want to be able to do this: Of note here: Some use cases I have in mind&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[],"class_list":["post-1771","post","type-post","status-publish","format-standard","hentry","category-cpp"],"_links":{"self":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1771","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1771"}],"version-history":[{"count":6,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1771\/revisions"}],"predecessor-version":[{"id":1779,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1771\/revisions\/1779"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1771"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1771"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1771"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}