mirror of
https://github.com/aantron/better-enums.git
synced 2025-12-06 08:46:42 +08:00
243 lines
9.3 KiB
HTML
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"> </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 — 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>
|
|
— 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 <iostream>
|
|
#include <enum.h>
|
|
typedef unsigned char uint8_t; // <cstdint> 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<html_color> { ...
|
|
namespace better_enums {
|
|
|
|
<em>template <>
|
|
struct integral_mapping<html_color> {
|
|
using integral_representation = unsigned int;
|
|
|
|
constexpr static html_color from_integral(unsigned int i)
|
|
{ return html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff); }
|
|
|
|
constexpr static unsigned int to_integral(html_color c)
|
|
{ return (unsigned int)c.r << 16 | (unsigned int)c.g << 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 << std::hex;
|
|
std::cout << "Red component: " << <em>(int)color->r</em> << std::endl;
|
|
std::cout << "Green component: " << <em>(int)color->g</em> << std::endl;
|
|
std::cout << "Blue component: " << <em>(int)color->b</em> << std::endl;
|
|
|
|
std::cout << <em>color._to_string()</em> << 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 << 16 | (unsigned int)g << 8 | b; }</em>
|
|
};
|
|
|
|
namespace better_enums {
|
|
|
|
template <>
|
|
struct integral_mapping<better_html_color> {
|
|
using integral_representation = unsigned int;
|
|
|
|
constexpr static better_html_color from_integral(unsigned int i)
|
|
{
|
|
return better_html_color(i >> 16 & 0xff, i >> 8 & 0xff, i & 0xff);
|
|
}
|
|
|
|
constexpr static unsigned int to_integral(better_html_color c)
|
|
{ return (unsigned int)c.r << 16 | (unsigned int)c.g << 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 © 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>
|
|
|