2025-01-12 19:51:34 +08:00

201 lines
6.3 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <unifex/config.hpp>
#include <unifex/overload.hpp>
#include <unifex/this.hpp>
#include <unifex/type_traits.hpp>
#include <type_traits>
#include <unifex/detail/prologue.hpp>
namespace unifex
{
namespace detail
{
// Queries about whether or not a given type, T, supports a given CPO.
template <typename T, typename CPO, typename Sig>
inline constexpr bool supports_type_erased_cpo_v = false;
template <typename T, typename CPO, typename Ret, typename... Args>
inline constexpr bool supports_type_erased_cpo_v<T, CPO, Ret(Args...)> =
is_callable_r_v<Ret, CPO, replace_this_t<Args, T>...>;
template <typename T, typename CPO, typename Ret, typename... Args>
inline constexpr bool
supports_type_erased_cpo_v<T, CPO, Ret(Args...) noexcept> =
is_nothrow_callable_r_v<Ret, CPO, replace_this_t<Args, T>...>;
template <typename T, typename... CPOs>
inline constexpr bool supports_type_erased_cpos_v =
(supports_type_erased_cpo_v<
T,
CPOs,
typename CPOs::type_erased_signature_t> && ...);
template <
typename CPO,
typename T,
bool NoExcept,
typename Ret,
typename... Args>
Ret _vtable_invoke(
CPO cpo,
replace_this_with_void_ptr_t<Args>... args) noexcept(NoExcept) {
static_assert(!NoExcept || noexcept(extract_this<Args...>{}(args...)));
void* thisPointer = extract_this<Args...>{}(args...);
static_assert(
!NoExcept ||
noexcept(Ret(((CPO &&) cpo)(replace_this<Args>::get(
(decltype(args)&&)args, *static_cast<T*>(thisPointer))...))));
return ((CPO &&) cpo)(replace_this<Args>::get(
(decltype(args)&&)args, *static_cast<T*>(thisPointer))...);
}
template <typename... CPOs>
struct inline_vtable_holder;
template <
typename CPO,
typename Sig = typename CPO::type_erased_signature_t>
struct vtable_entry;
template <typename CPO, typename Ret, typename... Args>
struct vtable_entry<CPO, Ret(Args...) noexcept> {
using fn_t =
Ret(base_cpo_t<CPO>, replace_this_with_void_ptr_t<Args>...) noexcept;
constexpr fn_t* get() const noexcept { return fn_; }
template <typename T>
static constexpr vtable_entry create() noexcept {
return vtable_entry{
&_vtable_invoke<base_cpo_t<CPO>, T, true, Ret, Args...>};
}
private:
template <typename... CPOs>
friend struct inline_vtable_holder;
explicit constexpr vtable_entry(fn_t* fn) noexcept : fn_(fn) {}
fn_t* fn_;
};
template <typename CPO, typename Ret, typename... Args>
struct vtable_entry<CPO, Ret(Args...)> {
using fn_t = Ret(base_cpo_t<CPO>, replace_this_with_void_ptr_t<Args>...);
constexpr fn_t* get() const noexcept { return fn_; }
template <typename T>
static constexpr vtable_entry create() noexcept {
return vtable_entry{
&_vtable_invoke<base_cpo_t<CPO>, T, false, Ret, Args...>};
}
private:
template <typename... CPOs>
friend struct inline_vtable_holder;
explicit constexpr vtable_entry(fn_t* fn) noexcept : fn_(fn) {}
fn_t* fn_;
};
template <typename... CPOs>
struct vtable : private vtable_entry<CPOs>... {
template <typename T>
static constexpr vtable create() noexcept {
return vtable{vtable_entry<CPOs>::template create<T>()...};
}
template <typename CPO>
constexpr auto get() const noexcept -> typename vtable_entry<CPO>::fn_t* {
const vtable_entry<CPO>& entry = *this;
return entry.get();
}
private:
friend inline_vtable_holder<CPOs...>;
explicit constexpr vtable(vtable_entry<CPOs>... entries) noexcept
: vtable_entry<CPOs>{entries}... {}
};
template <typename... CPOs>
struct indirect_vtable_holder {
template <typename T>
static indirect_vtable_holder create() {
static constexpr vtable<CPOs...> v =
vtable<CPOs...>::template create<T>();
return indirect_vtable_holder{v};
}
const vtable<CPOs...>& operator*() const noexcept { return *vtable_; }
const vtable<CPOs...>* operator->() const noexcept { return vtable_; }
private:
constexpr indirect_vtable_holder(const vtable<CPOs...>& vtable)
: vtable_(&vtable) {}
const vtable<CPOs...>* vtable_;
};
template <typename... CPOs>
struct inline_vtable_holder {
constexpr inline_vtable_holder(
const inline_vtable_holder& other) noexcept = default;
// Casting from an inline_vtable with a superset of vtable entries
template <typename... OtherCPOs>
/* implicit */ inline_vtable_holder(
const inline_vtable_holder<OtherCPOs...>& other) noexcept
: vtable_(vtable_entry<CPOs>(other->template get<CPOs>())...) {}
// Casting from an indirect_vtable with a superset of vtable entries
template <typename... OtherCPOs>
/* implicit */ inline_vtable_holder(
indirect_vtable_holder<OtherCPOs...> other) noexcept
: vtable_(vtable_entry<CPOs>(other->template get<CPOs>())...) {}
template <typename T>
static constexpr inline_vtable_holder create() {
return inline_vtable_holder{vtable<CPOs...>::template create<T>()};
}
const vtable<CPOs...>& operator*() const noexcept { return vtable_; }
const vtable<CPOs...>* operator->() const noexcept { return &vtable_; }
private:
constexpr inline_vtable_holder(const vtable<CPOs...>& vtable)
: vtable_(vtable) {}
vtable<CPOs...> vtable_;
};
} // namespace detail
} // namespace unifex
#include <unifex/detail/epilogue.hpp>