better-enums/demo/SemiQuine.html
2020-10-19 09:11:42 +03:00

226 lines
9.1 KiB
HTML

<!-- Generated automatically - edit the templates! -->
<!DOCTYPE html>
<html>
<head>
<title>Semi-quine - Better Enums</title>
<link rel="canonical" href="http://aantron.github.io/better-enums/demo/SemiQuine.html" />
<meta name="description" content="Have a Better Enum print its own definition. Shows how to
compute the amount of memory necessary from the reflective information provided
by a Better Enum." />
<meta name="author" content="Anton Bachin" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="../better-enums.css" />
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-62962513-1', 'auto');
ga('send', 'pageview');
</script>
</head>
<body class="">
<nav>
<div class="container">
<a class="first" href="https://raw.githubusercontent.com/aantron/better-enums/0.11.3/enum.h"
download>Download</a>
<a href="https://github.com/aantron/better-enums">GitHub</a>
<a href="../index.html">Home</a>
<a href="../tutorial/HelloWorld.html">Tutorial</a>
<a href="../ApiReference.html">Reference</a>
</div>
</nav>
<div class="spacer">&nbsp;</div>
<header>
<div class="container">
<section>
<h1><a href="../index.html">Better Enums</a></h1>
<h2>Reflective compile-time enums for <span class="cpp">C++</span></h2>
<h3>Open-source under the BSD license</h3>
</section>
<section class="notes">
<p>Version 0.11.3</p>
<p>To install, just add <code>enum.h</code> to your project.</p>
<p>
Visit the GitHub repo for issues, feedback, and the latest development.
</p>
</section>
<section class="buttons">
<a href="https://raw.githubusercontent.com/aantron/better-enums/0.11.3/enum.h"
download>Download <code>enum.h</code></a>
<a href="https://github.com/aantron/better-enums">GitHub</a>
</section>
</div>
</header>
<div class="main">
<div class="container">
<p>
This is an example of code you can write on top of Better Enums. It's a valid
program &mdash; you can <a href="https://github.com/aantron/better-enums/blob/0.11.3/example/104-quine.cc">download</a> it and try it out. The
program is also part of the test suite.
</p>
<h2>Semi-quine</h2>
<p>Let's make a Better Enum assemble its own definition in memory. It won't be
literally as defined, since we will lose the exact initializer expressions, but
we will be able to preserve the numeric values. We will reserve the memory
buffer for the definition at compile time.</p>
<p>Ok, so it's not really a quine, because we won't be writing all the code needed
to generate the definition to the buffer as well. And, there are better ways to
dump the definition than shown here. You could simply define a macro that
expands to an <code>BETTER_ENUM</code> declaration and also stringizes it.</p>
<p>But that's not the point here. The point of this page is to show some of the
reflective capabilities of Better Enums, so you can adapt them for cases where a
macro is not sufficient :)</p>
<p><a id="contents"></a><h3 class="contents">Contents</h3><ul class="contents"><li><a href="#ComputingTheSizeOfTheBuffer">Computing the size of the buffer</a></li><li><a href="#FormattingTheEnums">Formatting the enums</a></li><li><a href="#CheckingOurWork">Checking our work</a></li></ul></p>
<hr>
<pre>#include &lt;cassert&gt;
#include &lt;cstdio&gt;
#include &lt;iostream&gt;</pre><hr>
<p>First, we will need
<a href="../OptInFeatures.html#CompileTimeNameTrimming">full compile-time reflection</a>,
since we will be calling <code>_to_string</code>. Let's make sure it's enabled by defining
<code>BETTER_ENUMS_CONSTEXPR_TO_STRING</code> before including <code>enum.h</code>:</p>
<pre>#ifndef <em>BETTER_ENUMS_CONSTEXPR_TO_STRING</em>
#define <em>BETTER_ENUMS_CONSTEXPR_TO_STRING</em>
#endif
<em>#include &lt;enum.h&gt;</em></pre><p>Now, let's declare some enums to dump later:</p>
<pre>BETTER_ENUM(<em>Channel</em>, int, Red, Green, Blue)
BETTER_ENUM(<em>Depth</em>, int, TrueColor = 1, HighColor = 0)</pre><a id="ComputingTheSizeOfTheBuffer"></a><h3>Computing the size of the buffer</h3>
<p>First, we need to be able to get the length of each declaration above. We will
assume that the underlying type is always <code>int</code>, and that the spacing convention
is followed as above.</p>
<p>First, let's get the lengths of basic components:</p>
<pre>// Returns the length of the string representation of the number n
constexpr <em>size_t value_length</em>(int <em>n</em>, int bound = 10, size_t digits = 1)
{
return
<em>n &lt; bound ? digits : value_length(n, bound * 10, digits + 1)</em>;
}
// Returns the length of s
constexpr <em>size_t string_length</em>(const char *<em>s</em>, size_t index = 0)
{
return <em>s[index] == '\0' ? index : string_length(s, index + 1)</em>;
}</pre><p>Now, the length of the constant declaration. Here is where we lose information
about initializers. We are going to format the constant declarations like this:</p>
<pre class="comment">Red = 0, Green = 1, Blue = 2
TrueColor = 1, HighColor = 0</pre><p>This is because Better Enums doesn't provide a way to know what the exact
initializer was or whether there even was one &mdash; just the numeric value of
each constant. If we were trying to be clever, we could avoid formatting
initializers for sequential values, but I won't go through this exercise here.</p>
<pre>// Returns the length of the constants portion of the declaration of Enum,
// as described above.
template &lt;<em>typename Enum</em>&gt;
constexpr <em>size_t constants_length</em>(size_t index = 0, size_t accumulator = 0)
{
return
<em>index &gt;= Enum::_size() ? accumulator :
constants_length&lt;Enum&gt;(
index + 1, accumulator
+ string_length(", ")
+ string_length(Enum::_names()[index])
+ string_length(" = ")
+ value_length(
Enum::_values()[index]._to_integral()))</em>;
}</pre><p>Finally, we can combine these to get the length of the formatted declaration of
the whole enum:</p>
<pre>// Returns the length of the whole declaration of Enum, assuming the
// underlying type is int, and the constants are initialized as assumed by
// constants_length() above.
template &lt;<em>typename Enum</em>&gt;
constexpr <em>size_t declaration_length</em>()
{
return
<em>string_length("BETTER_ENUM(")
+ string_length(Enum::_name())
+ string_length(", int")
+ constants_length&lt;Enum&gt;()
+ string_length(")")</em>;
}</pre><a id="FormattingTheEnums"></a><h3>Formatting the enums</h3>
<p>Now, we can declare the buffers. The memory will be reserved at load time by the
binary's loader. The extra one byte in each buffer is for the null terminator.</p>
<pre><em>char</em> channel_definition[<em>declaration_length&lt;Channel&gt;() + 1</em>];
<em>char</em> depth_definition[<em>declaration_length&lt;Depth&gt;() + 1</em>];</pre><p>Let's also create the formatting function. This is executed at run time, but we
will be giving it pointers to our statically-allocated buffers. It will format
the enum declaration and then return the number of bytes it wrote to the buffer,
so that we can do a sanity check on it.</p>
<pre>template &lt;<em>typename Enum</em>&gt;
<em>size_t format</em>(char *<em>buffer</em>)
{
size_t offset = 0;
offset += std::sprintf(buffer, <em>"BETTER_ENUM(%s, int", Enum::_name()</em>);
<em>for</em> (<em>Enum value</em> : <em>Enum::_values()</em>) {
offset +=
std::sprintf(buffer + offset,
<em>", %s = %i",
value._to_string(), value._to_integral()</em>);
}
offset += std::sprintf(buffer + offset, <em>")"</em>);
return <em>offset</em>;
}</pre><a id="CheckingOurWork"></a><h3>Checking our work</h3>
<p>Now, we can write and run this code.</p>
<pre>int main()
{
size_t channel_length = <em>format&lt;Channel&gt;</em>(channel_definition);
assert(channel_length + 1 == sizeof(channel_definition));
size_t depth_length = <em>format&lt;Depth&gt;</em>(depth_definition);
assert(depth_length + 1 == sizeof(depth_definition));
std::cout &lt;&lt; <em>channel_definition</em> &lt;&lt; std::endl;
std::cout &lt;&lt; <em>depth_definition</em> &lt;&lt; std::endl;
return 0;
}</pre><p>It prints:</p>
<pre class="comment">BETTER_ENUM(Channel, int, Red = 0, Green = 1, Blue = 2)
BETTER_ENUM(Depth, int, TrueColor = 1, HighColor = 0)</pre>
<section class="tutorial-footer">
<p class="up">
Return to the <a href="../index.html#CompileTimeDemos">demo index</a>.
</p>
</section>
</div>
</div>
<footer>
<div class="container">
Copyright &copy; 2015-2019 Anton Bachin. Released under the BSD 2-clause
license. See
<a href="https://github.com/aantron/better-enums/blob/0.11.3/doc/LICENSE">
LICENSE</a>.
<br />
This page is part of the documentation for Better Enums 0.11.3.
</div>
</footer>
</body>
</html>