Skip to content

Instantly share code, notes, and snippets.

@imaami
Last active August 9, 2025 18:39
Show Gist options
  • Select an option

  • Save imaami/b7798a2a4316e8978579c7fc37fb44bc to your computer and use it in GitHub Desktop.

Select an option

Save imaami/b7798a2a4316e8978579c7fc37fb44bc to your computer and use it in GitHub Desktop.
C++14 tagged union
#define __STDC_FORMAT_MACROS
#include <cinttypes>
#include <cstdint>
#include <cstdio>
#include <string>
#include "tagged_union.hpp"
class Key {
std::string m_key;
public:
Key() noexcept : m_key{} {}
virtual ~Key() noexcept;
operator std::string const&() const noexcept { return m_key; }
std::string const& operator()() const noexcept { return m_key; }
Key& operator=(std::string const& key) { m_key = key; return *this; }
};
Key::~Key() noexcept { m_key.clear(); }
class I64 : public Key {
std::int64_t m_value;
public:
using value_type = decltype(m_value);
I64() noexcept : m_value{} {}
I64(value_type v) noexcept : m_value(v) {}
~I64() noexcept override;
operator value_type() const noexcept { return m_value; }
value_type operator()() const noexcept { return m_value; }
I64& operator=(value_type v) { m_value = v; return *this; }
};
I64::~I64() noexcept { m_value = 0; }
class U64 : public Key {
std::uint64_t m_value;
public:
using value_type = decltype(m_value);
U64() noexcept : m_value{} {}
~U64() noexcept override;
operator value_type() const noexcept { return m_value; }
value_type operator()() const noexcept { return m_value; }
U64& operator=(value_type v) { m_value = v; return *this; }
};
U64::~U64() noexcept { m_value = 0; }
enum { I64_TAG, U64_TAG };
using KeyValue = TaggedUnion<
Key,
Tag<I64_TAG, I64>,
Tag<U64_TAG, U64>
>;
int main()
{
auto *kv = new KeyValue(I64_TAG);
auto &v = kv->get<I64>();
Key &k = static_cast<Key&>(v);
k = "signed";
v = -64;
std::printf("%s: %" PRId64 "\n", k().c_str(), v());
delete kv;
}
#ifndef TAGGED_UNION_HPP_
#define TAGGED_UNION_HPP_
#include <cstddef>
#include <type_traits>
#include <algorithm>
#include <new>
template<typename, typename, typename...> class tagged_union;
template<typename Base, int... Tags, typename... Types>
class tagged_union<Base, std::integer_sequence<int, Tags...>, Types...>
{
static constexpr bool base_is_shared = std::min({
true, std::is_base_of<Base, Types>::value...
});
static_assert(base_is_shared,
"not all tagged types are derived from Base");
using buf_type = unsigned char[std::max({sizeof(Types)...})];
alignas(Types...) buf_type m_buf;
int m_tag;
public:
explicit tagged_union(int tag) noexcept : m_buf{}, m_tag(tag) {
static_cast<void>(std::initializer_list<bool>{
(m_tag == Tags
? ::new (reinterpret_cast<Types *>(&m_buf[0])) Types() != nullptr
: false)...
});
}
~tagged_union() {
static_cast<void>(std::initializer_list<bool>{
(m_tag == Tags
? (reinterpret_cast<Types *>(&m_buf[0])->~Types(), true)
: false)...
});
}
tagged_union(const tagged_union&) = delete;
tagged_union& operator=(const tagged_union&) = delete;
Base *operator->() noexcept {
Base *ret = nullptr;
static_cast<void>(std::initializer_list<bool>{
(m_tag == Tags
? (ret = static_cast<Base *>(
reinterpret_cast<Types *>(&m_buf[0]))
) != nullptr
: false)...
});
return ret;
}
template<typename T> T& get() noexcept {
return *reinterpret_cast<T *>(&m_buf[0]);
}
template<typename T> const T& get() const noexcept {
return *reinterpret_cast<T const *>(&m_buf[0]);
}
int tag() const noexcept { return m_tag; }
};
template<int I, typename T>
struct Tag {
static constexpr int tag = I;
using type = T;
};
template<typename Base, typename... Ts>
using TaggedUnion = tagged_union<
Base,
std::integer_sequence<
typename std::common_type<
decltype(Ts::tag)...
>::type,
Ts::tag...
>,
typename Ts::type...
>;
#endif // TAGGED_UNION_HPP_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment