Files
ts-qt/modern-qt/utility/wrapper/property.hh
2025-10-20 00:32:01 +08:00

163 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <tuple>
#include <utility>
// 少量样板代码的生成宏是可以接受的,
// 等 concept 可以作为参数的那一天,这个宏就可以废弃了
#define CREEPER_DEFINE_CHECKER(TRAIT) \
struct checker final { \
template <class T> \
static constexpr bool result = TRAIT<T>; \
};
namespace creeper {
/// CHECKER
template <class T>
concept checker_trait = requires {
{ T::template result<void> };
};
template <checker_trait... Ts>
struct CheckerOr {
template <class T>
static constexpr bool result = (Ts::template result<T> || ...);
};
template <checker_trait... Ts>
struct CheckerAnd {
template <class T>
static constexpr bool result = (Ts::template result<T> && ...);
};
/// PROP
template <class Token, typename T, auto interface>
struct SetterProp : Token {
T value;
constexpr explicit SetterProp(T value) noexcept
: value { std::move(value) } { }
constexpr explicit operator T(this auto&& self) noexcept { return self.value; }
template <typename O>
auto operator=(O&& other) noexcept -> SetterProp&
requires std::assignable_from<T&, O>
{
value = std::forward<O>(other);
return *this; // x= y =6
}
auto apply(auto& self) const noexcept -> void
requires requires { interface(self, std::declval<T>()); }
{
interface(self, value);
}
};
template <class Token, typename T, auto interface>
struct DerivedProp : T, Token {
using T::T;
template <typename... Args>
requires std::constructible_from<T, Args&&...>
constexpr explicit DerivedProp(Args&&... args)
: T(std::forward<Args>(args)...) { }
template <typename U>
requires requires(T& t, U&& u) { t = std::forward<U>(u); }
auto operator=(U&& value) -> DerivedProp& {
T::operator=(std::forward<U>(value));
return *this;
}
auto apply(this auto const& self, auto& widget) noexcept -> void {
interface(widget, static_cast<const T&>(self));
}
};
template <class Token, auto interface>
struct ActionProp : Token {
auto apply(auto& self) const noexcept -> void
requires requires { interface(self); }
{
interface(self);
}
};
/// @brief
/// 声明式包装,非侵入式实现 Setter 的声明式化
///
/// 该包装器支持将 std::tuple 与单个 prop 混合传入,不受顺序限制。内部通过
/// helper 检查 tuple 中的所有元素,递归调用 apply 将属性应用到 底层 Widget。
/// 利用 CheckerT 延迟模板实例化,在 concept 层面约束属性类型, 从而避免因
/// 递归参数展开而产生的海量且难以定位的模板错误。
///
/// @tparam W
/// 需要被包装的组件类型。
///
/// @tparam checker
/// 用于延迟模板实例化的“检查器”类型模板。典型形式如下:
/// struct checker final {
/// template <class T> struct result {
/// static constexpr auto v = concept<T>;
/// };
/// };
///
/// @note
/// 在不符合 checker 要求的类型被传入时,会在 concept 约束阶段直接报错,
/// 提供简洁且精准的编译期错误提示,避免编译器自动展开大量构造函数
/// 导致的冗长错误栈,但编译期报错信息依旧不友好。
///
template <class W, checker_trait checker>
struct Declarative : public W {
private:
// For helpe check single props
// 这里没法子塞一个 static_assert无论如何这里都会被尝试错误地实例化
template <typename T, class checker_>
static constexpr auto impl_props_trait = checker_::template result<std::remove_cvref_t<T>>;
// For help check tuple of props
// 使用 SFINAE 真是抱歉,没找到方便处理 tuple 的方法真是不好意思呢
template <typename T, class checker_>
static constexpr auto impl_tuple_trait = false;
template <typename... Ts, class checker_>
static constexpr auto impl_tuple_trait<std::tuple<Ts...>, checker_> =
(impl_props_trait<Ts, checker_> && ...);
public:
/* Export widget type */
using Widget = W;
/* Export checker type */
using Checker = checker;
/* For check props */
template <class T>
static constexpr auto props_trait = impl_props_trait<std::remove_cvref_t<T>, checker>;
/* For check tuple */
template <class T>
static constexpr auto tuple_trait = impl_tuple_trait<std::remove_cvref_t<T>, checker>;
public:
// 铁血的,热血的,冷血的声明式构造接口
explicit Declarative(auto&&... props) noexcept
requires((props_trait<decltype(props)> || tuple_trait<decltype(props)>) && ...)
: W {} {
(apply(std::forward<decltype(props)>(props)), ...);
}
auto apply(auto&& tuple) noexcept -> void
requires tuple_trait<decltype(tuple)>
{
std::apply([this](auto&&... args) { (apply(std::forward<decltype(args)>(args)), ...); },
std::forward<decltype(tuple)>(tuple));
}
auto apply(auto&& prop) noexcept -> void
requires props_trait<decltype(prop)>
{
std::forward<decltype(prop)>(prop).apply(*this);
}
};
}