type_safe_union : visit() and apply_to_contents() performance improvements (#2615)

This commit is contained in:
pfeatherstone 2022-07-03 01:43:00 +01:00 committed by GitHub
parent 7c32e1c18e
commit 3c73978de8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 94 deletions

View File

@ -9,7 +9,7 @@
#include <type_traits>
#include <functional>
#include "../serialize.h"
#include "../invoke.h"
#include "../utility.h"
namespace dlib
{
@ -139,41 +139,38 @@ namespace dlib
typename std::aligned_union<0, Types...>::type mem;
int type_identity = 0;
template<
typename F,
typename TSU,
std::size_t I
>
static void apply_to_contents_as_type(
F&& f,
TSU&& me
)
template<typename F, typename TSU>
struct dispatcher
{
std::forward<F>(f)(me.template unchecked_get<get_type_t<I>>());
constexpr static const std::size_t N = sizeof...(Types);
using R = decltype(std::declval<F>()(std::declval<TSU>().template unchecked_get<get_type_t<0>>()));
constexpr static const bool is_noexcept =
And<std::is_default_constructible<R>::value &&
noexcept(std::declval<F>()(std::declval<TSU>().template unchecked_get<Types>()))...>::value;
template<size_t I, typename std::enable_if<I == N, bool>::type = true>
inline R operator()(F&&, TSU&&, size_<I>)
noexcept(is_noexcept) { return R(); }
template<size_t I, typename std::enable_if<I < N, bool>::type = true>
inline R operator()(F&& f, TSU&& me, size_<I>)
noexcept(is_noexcept)
{
if (me.is_empty())
return R();
else if (me.get_current_type_id() == (I+1))
return std::forward<F>(f)(me.template unchecked_get<get_type_t<I>>());
else
return (*this)(std::forward<F>(f), std::forward<TSU>(me), size_<I+1>{});
}
template<
typename F,
typename TSU,
std::size_t... I
>
static void apply_to_contents_impl(
F&& f,
TSU&& me,
dlib::index_sequence<I...>
)
{
using func_t = void(*)(F&&, TSU&&);
const func_t vtable[] = {
/*! Empty (type_identity == 0) case !*/
[](F&&, TSU&&) {
},
/*! Non-empty cases !*/
&apply_to_contents_as_type<F&&,TSU&&,I>...
};
return vtable[me.get_current_type_id()](std::forward<F>(f), std::forward<TSU>(me));
template<typename F, typename TSU>
static inline auto dispatch(F&& f, TSU&& me)
noexcept(noexcept(dispatcher<F&&,TSU&&>{}(std::forward<F>(f), std::forward<TSU>(me), size_<0>{})))
-> decltype(dispatcher<F&&,TSU&&>{}(std::forward<F>(f), std::forward<TSU>(me), size_<0>{})) {
return dispatcher<F&&,TSU&&>{}(std::forward<F>(f), std::forward<TSU>(me), size_<0>{});
}
template <typename T>
@ -402,19 +399,19 @@ namespace dlib
}
template <typename F>
void apply_to_contents(
auto apply_to_contents(
F&& f
)
{
apply_to_contents_impl(std::forward<F>(f), *this, dlib::make_index_sequence<sizeof...(Types)>{});
) noexcept(noexcept(dispatch(std::forward<F>(f), std::declval<type_safe_union&>())))
-> decltype(dispatch(std::forward<F>(f), *this)) {
return dispatch(std::forward<F>(f), *this);
}
template <typename F>
void apply_to_contents(
auto apply_to_contents(
F&& f
) const
{
apply_to_contents_impl(std::forward<F>(f), *this, dlib::make_index_sequence<sizeof...(Types)>{});
) const noexcept(noexcept(dispatch(std::forward<F>(f), std::declval<const type_safe_union&>())))
-> decltype(dispatch(std::forward<F>(f), *this)) {
return dispatch(std::forward<F>(f), *this);
}
template <typename T>
@ -492,7 +489,7 @@ namespace dlib
{
if (type_identity == item.type_identity)
{
item.apply_to_contents(swap_to{*this});
apply_to_contents(swap_to{item});
}
else if (is_empty())
{
@ -540,48 +537,6 @@ namespace dlib
)...
};
}
template<
typename R,
typename F,
typename TSU,
std::size_t I
>
R visit_impl_as_type(
F&& f,
TSU&& tsu
)
{
using Tsu = typename std::decay<TSU>::type;
using T = type_safe_union_alternative_t<I, Tsu>;
return dlib::invoke(std::forward<F>(f), tsu.template cast_to<T>());
}
template<
typename R,
typename F,
typename TSU,
std::size_t... I
>
R visit_impl(
F&& f,
TSU&& tsu,
dlib::index_sequence<I...>
)
{
using func_t = R(*)(F&&, TSU&&);
const func_t vtable[] = {
/*! Empty (type_identity == 0) case !*/
[](F&&, TSU&&) {
return R();
},
/*! Non-empty cases !*/
&visit_impl_as_type<R,F&&,TSU&&,I>...
};
return vtable[tsu.get_current_type_id()](std::forward<F>(f), std::forward<TSU>(tsu));
}
}
template<
@ -598,20 +553,13 @@ namespace dlib
detail::for_each_type_impl(std::forward<F>(f), std::forward<TSU>(tsu), dlib::make_index_sequence<Size>{});
}
template<
typename F,
typename TSU,
typename Tsu = typename std::decay<TSU>::type,
typename T0 = type_safe_union_alternative_t<0, Tsu>
>
template<typename F, typename TSU>
auto visit(
F&& f,
TSU&& tsu
) -> dlib::invoke_result_t<F, decltype(tsu.template cast_to<T0>())>
{
using ReturnType = dlib::invoke_result_t<F, decltype(tsu.template cast_to<T0>())>;
static constexpr std::size_t Size = type_safe_union_size<Tsu>::value;
return detail::visit_impl<ReturnType>(std::forward<F>(f), std::forward<TSU>(tsu), dlib::make_index_sequence<Size>{});
) noexcept(noexcept(tsu.apply_to_contents(std::forward<F>(f))))
-> decltype(tsu.apply_to_contents(std::forward<F>(f))) {
return tsu.apply_to_contents(std::forward<F>(f));
}
namespace detail

View File

@ -124,6 +124,19 @@ namespace dlib
struct are_nothrow_swappable<T> : is_nothrow_swappable<T> {};
// ---------------------------------------------------------------------
template<bool First, bool... Rest>
struct And : std::integral_constant<bool, First && And<Rest...>::value> {};
template<bool Value>
struct And<Value> : std::integral_constant<bool, Value>{};
// ---------------------------------------------------------------------
template<std::size_t I>
using size_ = std::integral_constant<std::size_t, I>;
// ---------------------------------------------------------------------
}
#endif //DLIB_UTILITY_Hh_