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

View File

@ -124,6 +124,19 @@ namespace dlib
struct are_nothrow_swappable<T> : is_nothrow_swappable<T> {}; 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_ #endif //DLIB_UTILITY_Hh_