Skip to content

Instantly share code, notes, and snippets.

@sjolsen
Last active May 19, 2023 15:28
Show Gist options
  • Save sjolsen/6323929 to your computer and use it in GitHub Desktop.
Save sjolsen/6323929 to your computer and use it in GitHub Desktop.
Variadic, minimally type-checked unrestricted union in C++
/* Copyright 2021 Stuart Olsen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef UNRESTRICTED_UNION_HH
#define UNRESTRICTED_UNION_HH
#include <utility>
#include <type_traits>
typedef unsigned int union_index_type;
template <typename...>
struct variadic_union;
template <typename Head_>
struct variadic_union <Head_>
{
typedef Head_ Head;
alignas (Head) unsigned char data [sizeof (Head)];
//
template <typename T>
void construct (T&& head_init, union_index_type& union_index)
{
static_assert (std::is_same <T, Head>::value, "Tried to access non-existent type in union");
new (data) Head (std::forward <T> (head_init));
union_index = 0;
}
void destruct (union_index_type)
{
static_cast <Head*> (static_cast <void*> (data))->~Head ();
}
};
template <typename Head_, typename... Tail>
struct variadic_union <Head_, Tail...>
{
typedef Head_ Head;
union
{
alignas (Head) unsigned char data [sizeof (Head)];
variadic_union <Tail...> tail;
};
//
void construct (const Head& head_init, union_index_type& union_index)
{
new (data) Head (head_init);
union_index = 0;
}
void construct (Head& head_init, union_index_type& union_index)
{
const auto& head_cref = head_init;
construct (head_cref, union_index);
}
void construct (Head&& head_init, union_index_type& union_index)
{
using std::move;
new (data) Head (move (head_init));
union_index = 0;
}
template <typename T>
void construct (T&& t, union_index_type& union_index)
{
tail.construct (std::forward <T> (t), union_index);
++union_index;
}
//
void destruct (union_index_type union_index)
{
if (union_index == 0)
static_cast <Head*> (static_cast <void*> (data))->~Head ();
else
tail.destruct (union_index - 1);
}
};
template <typename...>
struct variadic_union_get;
template <typename Head, typename... Tail>
struct variadic_union_get <Head, variadic_union <Head, Tail...>>
{
static
Head& get (variadic_union <Head, Tail...>& vu)
{
return *static_cast <Head*> (static_cast <void*> (vu.data));
}
static
const Head& get (const variadic_union <Head, Tail...>& vu)
{
return *static_cast <Head*> (static_cast <void*> (vu.data));
}
static constexpr
const union_index_type index = 0;
};
template <typename Target, typename Head, typename... Tail>
struct variadic_union_get <Target, variadic_union <Head, Tail...>>
{
static
Target& get (variadic_union <Head, Tail...>& vu)
{
return variadic_union_get <Target, variadic_union <Tail...>>::get (vu.tail);
}
static
const Target& get (const variadic_union <Head, Tail...>& vu)
{
return variadic_union_get <Target, variadic_union <Tail...>>::get (vu.tail);
}
static constexpr
const union_index_type index = variadic_union_get <Target, variadic_union <Tail...>>::index + 1;
};
#include <stdexcept>
template <typename... Types>
class unrestricted_union
{
variadic_union <Types...> data;
union_index_type union_index;
public:
template <typename T>
unrestricted_union (T&& t)
{
data.construct (std::forward <T> (t), union_index);
}
~unrestricted_union ()
{
data.destruct (union_index);
}
//
template <typename T>
T& get ()
{
if (variadic_union_get <T, variadic_union <Types...>>::index != union_index)
throw std::logic_error ("Accessed wrong type in union");
return variadic_union_get <T, variadic_union <Types...>>::get (data);
}
template <typename T>
const T& get () const
{
if (variadic_union_get <T, variadic_union <Types...>>::index != union_index)
throw std::logic_error ("Accessed wrong type in union");
return variadic_union_get <T, variadic_union <Types...>>::get (data);
}
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment