#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef BLT_STD_VARIANT_H
#define BLT_STD_VARIANT_H
#include
#include
#include
#include
#include
#include
namespace blt
{
template
class variant_t;
namespace detail
{
template
struct filter_void;
template <>
struct filter_void<>
{
using type = std::tuple<>;
};
template
struct filter_void
{
using type = std::conditional_t<
std::is_same_v,
typename filter_void::type,
decltype(std::tuple_cat(
std::declval>(),
std::declval::type>()
))
>;
};
template
using filter_void_t = typename filter_void::type;
template
struct member_func_meta
{
using can_invoke = std::is_invocable;
using return_type = std::conditional_t, void>;
};
template
struct passthrough_value
{
using type = T;
using func = Func;
bool has_value = false;
explicit passthrough_value(const bool has_value): has_value(has_value)
{
}
explicit operator bool() const
{
return has_value;
}
};
template
struct first_invoke_member_func
{
constexpr static auto find_func()
{
return (... || []()
{
using Meta = member_func_meta;
if constexpr (Meta::can_invoke::value)
{
return passthrough_value{true};
}
return passthrough_value{false};
}());
}
using result = decltype(find_func());
using return_type = typename result::type;
using func_type = typename result::func;
};
template
struct member_func_detail;
template
struct member_func_detail
{
template
using return_type = std::invoke_result_t;
template
using can_invoke = std::is_invocable;
template
using result_or_void = std::conditional::value, return_type, void>;
constexpr static bool all_has_void = std::conjunction_v>...>;
using ret_type = std::conditional_t(
std::declval, return_type...>>()))>;
constexpr static bool all_has_ret = std::conjunction_v>...>;
constexpr static bool ret_or_void = std::conjunction_v>, std::is_void>>...>;
template
static constexpr auto make_variant(std::index_sequence)
{
if constexpr (all_has_void)
return;
using tuple_type = filter_void_t;
using variant = variant_t(std::declval()))...>;
return std::declval();
}
using variant_type = decltype(make_variant(std::index_sequence_for{}));
static constexpr auto make_return_type()
{
if constexpr (all_has_void)
return;
if constexpr (all_has_ret)
return std::declval();
if constexpr (ret_or_void)
return std::declval>();
return std::declval();
}
};
template
struct member_func_detail
{
};
}
/*
* std::visit(blt::lambda_visitor{
* lambdas...
* }, data_variant);
*/
template
struct lambda_visitor : TLambdas...
{
using TLambdas::operator()...;
};
#if __cplusplus < 202002L
// explicit deduction guide (not needed as of C++20)
template
lambda_visitor(TLambdas...) -> lambda_visitor;
#endif
template
class variant_t
{
public:
using value_type = std::variant;
size_t variant_size = sizeof...(Types);
constexpr variant_t(): m_variant()
{
}
constexpr variant_t(const variant_t& variant) noexcept(std::is_nothrow_copy_constructible_v): m_variant(variant.m_variant)
{
}
constexpr variant_t(variant_t&& variant) noexcept(std::is_nothrow_move_constructible_v): m_variant(std::move(variant.m_variant))
{
}
explicit constexpr variant_t(const value_type& variant) noexcept(std::is_nothrow_copy_constructible_v): m_variant(variant)
{
}
explicit constexpr variant_t(value_type&& variant) noexcept(std::is_nothrow_move_constructible_v): m_variant(std::move(variant))
{
}
explicit constexpr variant_t(Types&&... args) noexcept(std::is_nothrow_constructible_v): m_variant(
std::forward(args)...)
{
}
template
explicit constexpr variant_t(std::in_place_type_t, C_Args&&... args): m_variant(std::in_place_type, std::forward(args)...)
{
}
template
constexpr explicit variant_t(std::in_place_type_t, std::initializer_list il, C_Args&&... args): m_variant(
std::in_place_type, il, std::forward(args)...)
{
}
template
explicit constexpr variant_t(std::in_place_index_t, C_Args&&... args): m_variant(std::in_place_index, std::forward(args)...)
{
}
template
constexpr explicit variant_t(std::in_place_index_t, std::initializer_list il, C_Args&&... args): m_variant(
std::in_place_index, il, std::forward(args)...)
{
}
template
T& emplace(Args&&... args)
{
return m_variant.template emplace(std::forward(args)...);
}
template
T& emplace(std::initializer_list il, Args&&... args)
{
return m_variant.template emplace(il, std::forward(args)...);
}
template
std::variant_alternative_t& emplace(Args&&... args)
{
return m_variant.template emplace(std::forward(args)...);
}
template
std::variant_alternative_t& emplace(std::initializer_list il, Args&&... args)
{
return m_variant.template emplace(il, std::forward(args)...);
}
[[nodiscard]] constexpr std::size_t index() const noexcept
{
return m_variant.index();
}
[[nodiscard]] constexpr bool valueless_by_exception() const noexcept
{
return m_variant.valueless_by_exception();
}
template
constexpr auto visit(T&& visitor) -> decltype(auto)
{
return std::visit(std::forward(visitor), m_variant);
}
/**
* Automatic visitor generation with empty default behavior
* @param visitees user lambdas
*/
template
constexpr void visit_empty(Visitee&&... visitees)
{
std::visit(lambda_visitor{
std::forward(visitees)...,
[](auto)
{
}
}, m_variant);
}
template
constexpr auto visit_value(Default&& default_value, Visitee&&... visitees) -> decltype(auto)
{
return std::visit(lambda_visitor{
std::forward(visitees)...,
[default_value=std::forward(default_value)](auto&& value)
{
return std::forward(value);
}
});
}
template
constexpr auto visit_lambda(Default&& default_lambda, Visitee&&... visitees) -> decltype(auto)
{
return std::visit(lambda_visitor{
std::forward(visitees)...,
[default_lambda=std::forward(default_lambda)](auto&& value)
{
return std::forward(default_lambda)(std::forward(value));
}
});
}
/**
* Call a set of member functions on the types stored in the variant. If a type has more than one of these functions declared on it,
* the implementation will use the first member function provided. By default, if the stored value doesn't have any of the member functions,
* nothing will happen, if should_throw boolean is true, then the implementation will throw a runtime error.
* @tparam should_throw Controls if the implementation should throw if the type stored in the variant doesn't have any matching member function
* @return Result of calling the member functions. All functions should return the same value, otherwise this won't compile.
*/
template
constexpr auto call_member(const MemberFuncs... funcs)
{
static_assert(std::conjunction_v>...>,
"Must provide only pointers to member functions!");
return std::visit([=](auto&& value)
{
using ValueType = std::decay_t;
if constexpr (std::disjunction_v, ValueType>...>)
{
}
return *(... || ([&](auto&& func) -> decltype(auto)
{
using FuncType = std::decay_t;
using ReturnType = std::invoke_result_t;
if constexpr (std::is_invocable_v)
{
return std::make_optional(((value).*(func))());
}
return std::optional{};
}(cast_member_ptr>(std::forward(funcs)))));
}, m_variant);
}
template
constexpr auto call_member_args(const MemberFunc func, Args&&... args)
{
return std::visit([=](auto&& value)
{
return ((value).*(func))(std::forward(args)...);
}, m_variant);
}
template
[[nodiscard]] constexpr bool has_index() const noexcept
{
return m_variant.index() == I;
}
template
[[nodiscard]] constexpr bool has_type() const noexcept
{
return std::holds_alternative(m_variant);
}
template
[[nodiscard]] constexpr auto get() -> decltype(auto)
{
return std::get(m_variant);
}
template
[[nodiscard]] constexpr auto get() const -> decltype(auto)
{
return std::get(m_variant);
}
template
[[nodiscard]] constexpr auto get() -> decltype(auto)
{
return std::get(m_variant);
}
template
[[nodiscard]] constexpr auto get() const -> decltype(auto)
{
return std::get(m_variant);
}
template
constexpr std::add_pointer_t> get_if() noexcept
{
return std::get_if(m_variant);
}
template
constexpr std::add_pointer_t> get_if() noexcept
{
return std::get_if(m_variant);
}
template
constexpr std::add_pointer_t get_if() noexcept
{
return std::get_if(m_variant);
}
template
constexpr std::add_pointer_t get_if() noexcept
{
return std::get_if(m_variant);
}
template
constexpr T value_or(T&& t) const
{
if (has_type())
return get();
return std::forward(t);
}
template
constexpr std::variant_alternative_t value_or(const std::variant_alternative_t& t) const
{
if (has_type>())
return get();
return t;
}
template
constexpr std::variant_alternative_t value_or(std::variant_alternative_t&& t) const
{
if (has_type>())
return get();
return t;
}
template
constexpr const value_type& variant() const
{
return m_variant;
}
constexpr value_type& variant()
{
return m_variant;
}
[[nodiscard]] constexpr size_t size() const
{
return variant_size;
}
friend bool operator==(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant == rhs.m_variant;
}
friend bool operator!=(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant != rhs.m_variant;
}
friend bool operator<(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant < rhs.m_variant;
}
friend bool operator>(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant > rhs.m_variant;
}
friend bool operator<=(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant <= rhs.m_variant;
}
friend bool operator>=(const variant_t& lhs, const variant_t& rhs)
{
return lhs.m_variant >= rhs.m_variant;
}
private:
template
static auto cast_member_ptr(ReturnType (Base::*base_func)(Args...))
{
return reinterpret_cast(base_func);
}
value_type m_variant;
};
namespace detail
{
template
class variant_is_base_of
{
};
template
class variant_is_base_of>
{
public:
using value_type = bool;
template
static constexpr bool value = std::conjunction_v...>;
};
template
class variant_is_base_of>
{
public:
using value_type = bool;
template
static constexpr bool value = std::conjunction_v...>;
};
template
static constexpr bool variant_is_base_of_v = variant_is_base_of::value;
}
}
#endif //BLT_STD_VARIANT_H