I'm not entirely sure why function_ref_base declares its destructor. I guess it's declared in order to make it protected, because the class is non-polymorphic and inherited by function_ref, and one may want to avoid slicing. On the other hand, function_ref_base is an implementation detail, living in a `detail` namespace; so that sounds very unlikely. In any case, the class is clearly meant to be copiable, so add back (as protected) the missing special members. Pick-to: 6.8 6.5 Change-Id: Ibf909e1746e65eecf8b8990839a6e4c9eb56ca13 Reviewed-by: Marc Mutz <marc.mutz@qt.io> (cherry picked from commit 74b875641f40b15a4a59a0c55cd79c251b49c05e) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
200 lines
6.2 KiB
C++
200 lines
6.2 KiB
C++
// Copyright (C) 2022 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
#ifndef QXPFUNCTIONAL_H
|
|
#define QXPFUNCTIONAL_H
|
|
|
|
#include <QtCore/qglobal.h>
|
|
|
|
//
|
|
// W A R N I N G
|
|
// -------------
|
|
//
|
|
// This file is not part of the Qt API. Types and functions defined in this
|
|
// file can reliably be replaced by their std counterparts, once available.
|
|
// You may use these definitions in your own code, but be aware that we
|
|
// will remove them once Qt depends on the C++ version that supports
|
|
// them in namespace std. There will be NO deprecation warning, the
|
|
// definitions will JUST go away.
|
|
//
|
|
// If you can't agree to these terms, don't use these definitions!
|
|
//
|
|
// We mean it.
|
|
//
|
|
|
|
#include <QtCore/q23functional.h>
|
|
#include <QtCore/q20type_traits.h>
|
|
#include <utility>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
namespace qxp {
|
|
// like P0792r9's function_ref:
|
|
|
|
// [func.wrap.ref], non-owning wrapper
|
|
template<class... S> class function_ref; // not defined
|
|
|
|
// template<class R, class... ArgTypes>
|
|
// class function_ref<R(ArgTypes...) cv noexcept(noex)>; // see below
|
|
//
|
|
// [func.wrap.ref.general]
|
|
// The header provides partial specializations of function_ref for each combination
|
|
// of the possible replacements of the placeholders cv and noex where:
|
|
// - cv is either const or empty.
|
|
// - noex is either true or false.
|
|
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
using if_function = std::enable_if_t<std::is_function_v<T>, bool>;
|
|
template <typename T>
|
|
using if_non_function = std::enable_if_t<!std::is_function_v<T>, bool>;
|
|
|
|
template <typename From, typename To>
|
|
using copy_const_t = std::conditional_t<
|
|
std::is_const_v<From>,
|
|
std::add_const_t<To>,
|
|
To
|
|
>;
|
|
|
|
template <class Const>
|
|
union BoundEntityType {
|
|
template <typename F, if_function<F> = true>
|
|
explicit constexpr BoundEntityType(F *f)
|
|
: fun(reinterpret_cast<QFunctionPointer>(f)) {}
|
|
template <typename T, if_non_function<T> = true>
|
|
explicit constexpr BoundEntityType(T *t)
|
|
: obj(static_cast<Const*>(t)) {}
|
|
Const *obj;
|
|
QFunctionPointer fun;
|
|
};
|
|
|
|
template <bool noex, class Const, class R, class... ArgTypes>
|
|
class function_ref_base
|
|
{
|
|
protected:
|
|
~function_ref_base() = default;
|
|
function_ref_base(const function_ref_base &) = default;
|
|
function_ref_base(function_ref_base &&) = default;
|
|
function_ref_base &operator=(const function_ref_base &) = default;
|
|
function_ref_base &operator=(function_ref_base &&) = default;
|
|
|
|
using BoundEntityType = detail::BoundEntityType<Const>;
|
|
|
|
template <typename... Ts>
|
|
using is_invocable_using = std::conditional_t<
|
|
noex,
|
|
std::is_nothrow_invocable_r<R, Ts..., ArgTypes...>,
|
|
std::is_invocable_r<R, Ts..., ArgTypes...>
|
|
>;
|
|
|
|
using ThunkPtr = R(*)(BoundEntityType, ArgTypes&&...) noexcept(noex);
|
|
|
|
BoundEntityType m_bound_entity;
|
|
ThunkPtr m_thunk_ptr;
|
|
|
|
public:
|
|
template<
|
|
class F,
|
|
std::enable_if_t<std::conjunction_v<
|
|
std::is_function<F>,
|
|
is_invocable_using<F>
|
|
>, bool> = true
|
|
>
|
|
Q_IMPLICIT function_ref_base(F* f) noexcept
|
|
: m_bound_entity(f),
|
|
m_thunk_ptr([](BoundEntityType ctx, ArgTypes&&... args) noexcept(noex) -> R {
|
|
return q23::invoke_r<R>(reinterpret_cast<F*>(ctx.fun),
|
|
std::forward<ArgTypes>(args)...);
|
|
})
|
|
{}
|
|
|
|
template<
|
|
class F,
|
|
std::enable_if_t<std::conjunction_v<
|
|
std::negation<std::is_same<q20::remove_cvref_t<F>, function_ref_base>>,
|
|
#ifdef Q_OS_VXWORKS
|
|
// The VxWorks compiler is trying to match this ctor against
|
|
// qxp::function_ref in lieu of using the copy-constructor, so ban
|
|
// matching against the equivalent qxp::function_ref here.
|
|
// This doesn't change anything on other platforms, so to save
|
|
// on compile-speed, enable it only for VxWorks:
|
|
std::negation<
|
|
std::is_same<
|
|
q20::remove_cvref_t<F>,
|
|
std::conditional_t<
|
|
std::is_const_v<Const>,
|
|
qxp::function_ref<R(ArgTypes...) const noexcept(noex)>,
|
|
qxp::function_ref<R(ArgTypes...) noexcept(noex)>
|
|
>
|
|
>
|
|
>,
|
|
#endif // Q_OS_VXWORKS
|
|
std::negation<std::is_member_pointer<std::remove_reference_t<F>>>,
|
|
is_invocable_using<copy_const_t<Const, std::remove_reference_t<F>>&>
|
|
>, bool> = true
|
|
>
|
|
Q_IMPLICIT constexpr function_ref_base(F&& f) noexcept
|
|
: m_bound_entity(std::addressof(f)),
|
|
m_thunk_ptr([](BoundEntityType ctx, ArgTypes&&... args) noexcept(noex) -> R {
|
|
using That = copy_const_t<Const, std::remove_reference_t<F>>;
|
|
return q23::invoke_r<R>(*static_cast<That*>(ctx.obj),
|
|
std::forward<ArgTypes>(args)...);
|
|
})
|
|
{}
|
|
|
|
protected:
|
|
template <
|
|
class T,
|
|
std::enable_if_t<std::negation_v<
|
|
std::disjunction<
|
|
std::is_same<T, function_ref_base>,
|
|
std::is_pointer<T>
|
|
>
|
|
>, bool> = true
|
|
>
|
|
function_ref_base& operator=(T) = delete;
|
|
|
|
// Invocation [func.wrap.ref.inv]
|
|
R operator()(ArgTypes... args) const noexcept(noex)
|
|
{
|
|
return m_thunk_ptr(m_bound_entity, std::forward<ArgTypes>(args)...);
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
#define QT_SPECIALIZE_FUNCTION_REF(cv, noex) \
|
|
template<class R, class... ArgTypes> \
|
|
class function_ref<R(ArgTypes...) cv noexcept( noex )> \
|
|
: private detail::function_ref_base< noex , cv void, R, ArgTypes...> \
|
|
{ \
|
|
using base = detail::function_ref_base< noex , cv void, R, ArgTypes...>; \
|
|
\
|
|
public: \
|
|
using base::base; \
|
|
using base::operator(); \
|
|
} \
|
|
/* end */
|
|
|
|
QT_SPECIALIZE_FUNCTION_REF( , false);
|
|
QT_SPECIALIZE_FUNCTION_REF(const, false);
|
|
QT_SPECIALIZE_FUNCTION_REF( , true );
|
|
QT_SPECIALIZE_FUNCTION_REF(const, true );
|
|
|
|
#undef QT_SPECIALIZE_FUNCTION_REF
|
|
|
|
// deduction guides [func.wrap.ref.deduct]
|
|
|
|
template <
|
|
class F,
|
|
detail::if_function<F> = true
|
|
>
|
|
function_ref(F*) -> function_ref<F>;
|
|
|
|
} // namespace qxp
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#endif /* QXPFUNCTIONAL_H */
|