mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-06 16:56:42 +08:00
226 lines
9.1 KiB
HTML
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"> </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 — 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 <cassert>
|
|
#include <cstdio>
|
|
#include <iostream></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 <enum.h></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 < 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 — 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 <<em>typename Enum</em>>
|
|
constexpr <em>size_t constants_length</em>(size_t index = 0, size_t accumulator = 0)
|
|
{
|
|
return
|
|
<em>index >= Enum::_size() ? accumulator :
|
|
constants_length<Enum>(
|
|
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 <<em>typename Enum</em>>
|
|
constexpr <em>size_t declaration_length</em>()
|
|
{
|
|
return
|
|
<em>string_length("BETTER_ENUM(")
|
|
+ string_length(Enum::_name())
|
|
+ string_length(", int")
|
|
+ constants_length<Enum>()
|
|
+ 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<Channel>() + 1</em>];
|
|
<em>char</em> depth_definition[<em>declaration_length<Depth>() + 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 <<em>typename Enum</em>>
|
|
<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<Channel></em>(channel_definition);
|
|
assert(channel_length + 1 == sizeof(channel_definition));
|
|
|
|
size_t depth_length = <em>format<Depth></em>(depth_definition);
|
|
assert(depth_length + 1 == sizeof(depth_definition));
|
|
|
|
std::cout << <em>channel_definition</em> << std::endl;
|
|
std::cout << <em>depth_definition</em> << 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 © 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>
|
|
|