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

243 lines
9.3 KiB
HTML

<!-- Generated automatically - edit the templates! -->
<!DOCTYPE html>
<html>
<head>
<title>Non-integral underlying types - Better Enums</title>
<link rel="canonical" href="http://aantron.github.io/better-enums/demo/NonIntegralUnderlyingTypes.html" />
<meta name="description" content=""Using Better Enums with non-integral underlying types."" />
<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/master/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>
<a href="../Contact.html">Contact</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.10.1</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/master/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 page is an advanced demo showing the kind 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/master/example/102-any-underlying.cc">download</a> it and try it out. The program runs as part of
the automated test suite.
</p>
<h2>Non-integral underlying types</h2>
<p>The underlying type of a Better Enum doesn't have to be an integral type. It can
be any literal type <code>T</code>, as long as you provide a <code>constexpr</code> two-way mapping
between <code>T</code> and an integral type of your choosing. This also works in <span class="cpp">C++</span><span class="eleven">98</span>
&mdash; though then, of course, <code>T</code> doesn't have to be literal and the mapping
doesn't have to be <code>constexpr</code>. In <span class="cpp">C++</span><span class="eleven">98</span>, everything involving <code>T</code> will simply
be done by Better Enums at run time.</p>
<p>This feature is semi-experimental. I am considering relaxing the requirements on
<code>T</code> so that it doesn't have to be literal. I can use a <code>reinterpret_cast</code> to
make a mapping automatically. This will make non-integral underlying types
easier to use, but will also prevent usage at compile time, which unfortunately
has structural consequences for the implementation of Better Enums, and
additional semantic consequences for usage, even at run time.</p>
<p>In the meantime, here's how to have a non-integral underlying type in the
current version.</p>
<pre>#include &lt;iostream&gt;
#include &lt;enum.h&gt;
typedef unsigned char uint8_t; // &lt;cstdint&gt; not in C++98.
// The underlying type. A color triplet.
<em>struct html_color {
uint8_t r, g, b;
constexpr html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
r(_r), g(_g), b(_b) { }
};</em>
// The mapping. It just stuffs bits to get the same effect as
// reinterpret_cast, except reinterpret_cast is not available in constexpr
// functions, so we have to write the bit manipulations out. On modern
// C++11 compilers, you don't have to enter the better_enums namespace like
// this - you can just do
// struct ::better_enums::integral_mapping&lt;html_color&gt; { ...
namespace better_enums {
<em>template &lt;&gt;
struct integral_mapping&lt;html_color&gt; {
using integral_representation = unsigned int;
constexpr static html_color from_integral(unsigned int i)
{ return html_color(i &gt;&gt; 16 &amp; 0xff, i &gt;&gt; 8 &amp; 0xff, i &amp; 0xff); }
constexpr static unsigned int to_integral(html_color c)
{ return (unsigned int)c.r &lt;&lt; 16 | (unsigned int)c.g &lt;&lt; 8 | c.b; }
};</em>
}
// The enum itself.
<em>ENUM(Color, html_color,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954)</em></pre><p>Now, we can do:</p>
<pre>int main()
{
<em>Color color = Color::darksalmon</em>;
std::cout &lt;&lt; std::hex;
std::cout &lt;&lt; "Red component: " &lt;&lt; <em>(int)color-&gt;r</em> &lt;&lt; std::endl;
std::cout &lt;&lt; "Green component: " &lt;&lt; <em>(int)color-&gt;g</em> &lt;&lt; std::endl;
std::cout &lt;&lt; "Blue component: " &lt;&lt; <em>(int)color-&gt;b</em> &lt;&lt; std::endl;
std::cout &lt;&lt; <em>color._to_string()</em> &lt;&lt; std::endl;
<em>switch (color)</em> {
<em>case Color::darksalmon</em>: return 0;
<em>case Color::purplemimosa</em>: return 1;
<em>case Color::slimegreen</em>: return 2;
}
return 0;
}</pre><p>This prints each component, the name of the color (<code>"darksalmon"</code>), and then
exits from the <code>switch</code> with status 0.</p>
<a id="ConstructorsInInitializers"></a><h3>Constructors in initializers</h3>
<p>The above declaration used only numbers in initializers, but it is actually
possible to use constructors of <code>html_color</code>. We have to add a <code>constexpr</code>
converting operator directly to <code>html_color</code>, however:</p>
<pre class="comment">struct better_html_color {
uint8_t r, g, b;
constexpr better_html_color(uint8_t _r, uint8_t _g, uint8_t _b) :
r(_r), g(_g), b(_b) { }
<em>// This is new:
constexpr operator unsigned int() const
{ return (unsigned int)r &lt;&lt; 16 | (unsigned int)g &lt;&lt; 8 | b; }</em>
};
namespace better_enums {
template &lt;&gt;
struct integral_mapping&lt;better_html_color&gt; {
using integral_representation = unsigned int;
constexpr static better_html_color from_integral(unsigned int i)
{
return better_html_color(i &gt;&gt; 16 &amp; 0xff, i &gt;&gt; 8 &amp; 0xff, i &amp; 0xff);
}
constexpr static unsigned int to_integral(better_html_color c)
{ return (unsigned int)c.r &lt;&lt; 16 | (unsigned int)c.g &lt;&lt; 8 | c.b; }
};
}</pre><p>This allows:</p>
<pre class="comment">ENUM(BetterColor, better_html_color,
darksalmon = 0xc47451, purplemimosa = 0x9e7bff, slimegreen = 0xbce954,
<em>celeste = better_html_color(0x50, 0xeb, 0xec)</em>)</pre><p>If you can't edit your literal type to add this converting operator, or don't
want to for type safety reasons, you can achieve a similar effect by declaring
an intermediate type <code>U</code> that <code>html_color</code> can convert to, that can convert to
the integral type. Then, cast your constructor call to <code>U</code>. The type <code>U</code> is for
declarations only.</p>
<p>Constructors in initializers require <span class="cpp">C++</span><span class="eleven">11</span>. Also, g++ doesn't support this
before 5.1.</p>
<a id="LettingTheCompilerEnumerateYourType"></a><h3>Letting the compiler enumerate your type</h3>
<p>Of course, as long as the values are valid, you can let the compiler enumerate
your type as in a regular enum, by omitting initializers:</p>
<pre class="comment"><em>ENUM(FD, file_descriptor, STDIN, STDOUT, STDERR, SomePipeYourDaemonHas, ...)</em></pre><p>Here, <code>FD::STDIN</code> maps to the integral representation 0, <code>STDOUT</code> to 1, and so
on.</p>
<a id="Discussion"></a><h3>Discussion</h3>
<p>This feature is still semi-experimental, though I expect it to remain stable,
except perhaps that I will make it possible to infer the type
<code>integral_representation</code>.</p>
<p>Any opinions are welcome.</p>
<ul>
<li>The main reason Better Enums needs you to supply and explicit mapping is
because it can't just get the "bits" of objects of underlying type in
<code>constexpr</code> code. Both <code>reinterpret_cast</code> and union abuse seem to be forbidden
in <code>constexpr</code> functions.</li>
<li>There is currently no way to have two different integral representaitons for
the same underlying type in different enums. I don't think that's a major use
case at this point, however.</li>
</ul>
<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 Anton Bachin. Released under the BSD 2-clause license.
See
<a href="https://github.com/aantron/better-enums/blob/master/doc/LICENSE">
LICENSE</a>.
<br />
This page is part of the documentation for Better Enums 0.10.1.
</div>
</footer>
</body>
</html>