srsLTE/lib/include/srsran/adt/choice_type.h

246 lines
6.7 KiB
C++

/**
* Copyright 2013-2022 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsRAN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srsran/adt/detail/type_utils.h"
#ifndef SRSRAN_CHOICE_TYPE_H
#define SRSRAN_CHOICE_TYPE_H
namespace srsran {
//! Compute maximum at compile time
template <std::size_t arg1, std::size_t... others>
struct static_max;
template <std::size_t arg>
struct static_max<arg> {
static const std::size_t value = arg;
};
template <std::size_t arg1, std::size_t arg2, std::size_t... others>
struct static_max<arg1, arg2, others...> {
static const std::size_t value =
arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
};
namespace choice_details {
//! Holds one of the Args types
template <std::size_t MaxSize, std::size_t MaxAlign>
struct choice_storage_t {
static const std::size_t max_size = MaxSize;
static const std::size_t max_align = MaxAlign;
using buffer_t = typename std::aligned_storage<max_size, max_align>::type;
buffer_t buffer;
void* get_buffer() { return &buffer; }
template <typename T>
T& get_unchecked()
{
return *reinterpret_cast<T*>(&buffer);
}
template <typename T>
const T& get_unchecked() const
{
return *reinterpret_cast<const T*>(&buffer);
}
template <typename U>
void destroy_unchecked()
{
get_unchecked<U>().~U();
};
};
/*************************
* Tagged Union Helpers
************************/
template <typename C>
struct CopyCtorVisitor {
explicit CopyCtorVisitor(C* c_) : c(c_) {}
template <typename T>
void operator()(const T& t)
{
c->construct_unchecked(t);
}
C* c;
};
template <typename C>
struct MoveCtorVisitor {
explicit MoveCtorVisitor(C* c_) : c(c_) {}
template <typename T>
void operator()(T&& t)
{
c->construct_unchecked(std::move(t));
}
C* c;
};
template <typename C>
struct DtorUncheckVisitor {
explicit DtorUncheckVisitor(C* c_) : c(c_) {}
template <typename T>
void operator()(T& t)
{
c->template destroy_unchecked<T>();
}
C* c;
};
template <typename... Args>
struct tagged_union_t;
template <typename... Args>
struct tagged_union_t
: private choice_storage_t<static_max<sizeof(Args)...>::value, static_max<alignof(Args)...>::value> {
using base_t = choice_storage_t<static_max<sizeof(Args)...>::value, static_max<alignof(Args)...>::value>;
using buffer_t = typename base_t::buffer_t;
using this_type = tagged_union_t<Args...>;
std::size_t type_id;
using base_t::destroy_unchecked;
using base_t::get_buffer;
using base_t::get_unchecked;
template <typename U, typename... Args2>
void construct_emplace_unchecked(Args2&&... args)
{
using U2 = typename std::decay<U>::type;
static_assert(type_list_contains<U2, Args...>(),
"The provided type to ctor is not part of the list of possible types");
type_id = get_type_index<U2, Args...>();
new (get_buffer()) U2(std::forward<Args2>(args)...);
}
template <typename U>
void construct_unchecked(U&& u)
{
using U2 = typename std::decay<U>::type;
static_assert(type_list_contains<U2, Args...>(),
"The provided type to ctor is not part of the list of possible types");
type_id = get_type_index<U2, Args...>();
new (get_buffer()) U2(std::forward<U>(u));
}
void copy_unchecked(const this_type& other) { visit(CopyCtorVisitor<this_type>{this}, other); }
void move_unchecked(this_type&& other) { visit(MoveCtorVisitor<this_type>{this}, other); }
void dtor_unchecked() { visit(choice_details::DtorUncheckVisitor<base_t>{this}, *this); }
size_t get_type_idx() const { return type_id; }
template <typename T>
bool is() const
{
return get_type_index<T, Args...>() == type_id;
}
template <typename T>
constexpr static bool can_hold_type()
{
return type_list_contains<T, Args...>();
}
};
} // namespace choice_details
template <typename... Args>
class choice_t : private choice_details::tagged_union_t<Args...>
{
using base_t = choice_details::tagged_union_t<Args...>;
public:
using default_type = typename get_index_type<0, Args...>::type;
//! Useful metafunction
template <typename T>
using enable_if_can_hold =
typename std::enable_if<base_t::template can_hold_type<typename std::decay<T>::type>()>::type;
template <typename T>
using disable_if_can_hold =
typename std::enable_if<not base_t::template can_hold_type<typename std::decay<T>::type>()>::type;
using base_t::can_hold_type;
using base_t::get_unchecked;
using base_t::is;
template <typename... Args2,
typename = typename std::enable_if<std::is_constructible<default_type, Args2...>::value>::type>
explicit choice_t(Args2&&... args) noexcept
{
base_t::template construct_emplace_unchecked<default_type>(std::forward<Args2>(args)...);
}
choice_t(const choice_t<Args...>& other) noexcept { base_t::copy_unchecked(other); }
choice_t(choice_t<Args...>&& other) noexcept { base_t::move_unchecked(std::move(other)); }
template <typename U, typename = enable_if_can_hold<U> >
choice_t(U&& u) noexcept
{
base_t::construct_unchecked(std::forward<U>(u));
}
~choice_t() { base_t::dtor_unchecked(); }
template <typename U, typename = enable_if_can_hold<U> >
choice_t& operator=(U&& u) noexcept
{
if (not base_t::template is<U>()) {
base_t::dtor_unchecked();
}
base_t::construct_unchecked(std::forward<U>(u));
return *this;
}
template <typename U, typename... Args2>
void emplace(Args2&&... args) noexcept
{
base_t::dtor_unchecked();
base_t::template construct_emplace_unchecked<U>(std::forward<Args2>(args)...);
}
choice_t& operator=(const choice_t& other) noexcept
{
if (this != &other) {
base_t::dtor_unchecked();
base_t::copy_unchecked(other);
}
return *this;
}
choice_t& operator=(choice_t&& other) noexcept
{
base_t::dtor_unchecked();
base_t::move_unchecked(std::move(other));
return *this;
}
bool holds_same_type(const choice_t& other) noexcept { return base_t::type_id == other.type_id; }
};
} // namespace srsran
#endif // SRSRAN_CHOICE_TYPE_H