AK: Allow derived types to create base badges

Derived classes can now mint their own badge and pass it to APIs that
accept a badge for an authorized base class.
This commit is contained in:
Sam Atkins 2026-06-10 12:04:27 +01:00 committed by Jelle Raaijmakers
parent ad3d65b834
commit 071b4f7062
2 changed files with 121 additions and 2 deletions

View file

@ -8,9 +8,18 @@
#include <AK/Noncopyable.h>
#include <AK/Platform.h>
#include <AK/StdLibExtraDetails.h>
namespace AK {
namespace Detail {
template<typename AuthorizedType, typename BadgeType>
inline constexpr bool IsBadgeType = IsSame<AuthorizedType, BadgeType>
|| IsBaseOf<AuthorizedType, BadgeType>;
}
template<typename...>
class Badge;
@ -22,6 +31,14 @@ class Badge<T> {
public:
using Type = T;
// Friendship is not inherited, so derived classes mint Badge<Derived> and
// convert it to Badge<Base>.
template<typename U>
requires(IsBaseOf<T, U> && !IsSame<T, U>)
constexpr Badge(Badge<U> const&)
{
}
private:
friend T;
constexpr Badge() = default;
@ -34,6 +51,13 @@ class Badge<T, U> {
AK_MAKE_NONCOPYABLE(Badge);
AK_MAKE_NONMOVABLE(Badge);
public:
template<typename V>
requires(Detail::IsBadgeType<T, V> || Detail::IsBadgeType<U, V>)
constexpr Badge(Badge<V> const&)
{
}
private:
friend T;
friend U;
@ -45,6 +69,14 @@ class Badge<T, U, V> {
AK_MAKE_NONCOPYABLE(Badge);
AK_MAKE_NONMOVABLE(Badge);
public:
template<typename W>
requires(Detail::IsBadgeType<T, W> || Detail::IsBadgeType<U, W>
|| Detail::IsBadgeType<V, W>)
constexpr Badge(Badge<W> const&)
{
}
private:
friend T;
friend U;
@ -57,6 +89,14 @@ class Badge<T, U, V, W> {
AK_MAKE_NONCOPYABLE(Badge);
AK_MAKE_NONMOVABLE(Badge);
public:
template<typename X>
requires(Detail::IsBadgeType<T, X> || Detail::IsBadgeType<U, X>
|| Detail::IsBadgeType<V, X> || Detail::IsBadgeType<W, X>)
constexpr Badge(Badge<X> const&)
{
}
private:
friend T;
friend U;
@ -70,6 +110,15 @@ class Badge<T, U, V, W, X> {
AK_MAKE_NONCOPYABLE(Badge);
AK_MAKE_NONMOVABLE(Badge);
public:
template<typename Y>
requires(Detail::IsBadgeType<T, Y> || Detail::IsBadgeType<U, Y>
|| Detail::IsBadgeType<V, Y> || Detail::IsBadgeType<W, Y>
|| Detail::IsBadgeType<X, Y>)
constexpr Badge(Badge<Y> const&)
{
}
private:
friend T;
friend U;

View file

@ -36,6 +36,16 @@ struct MultipleBadgeUserE {
struct UnrelatedBadgeUser {
};
struct BaseBadgeUser {
static void call_base_badge();
static void call_multi_base_badge();
};
struct DerivedBadgeUser : BaseBadgeUser {
static void call_base_badge();
static void call_multi_base_badge();
};
static void accepts_two_argument_badge(Badge<MultipleBadgeUserA, MultipleBadgeUserB>)
{
}
@ -52,6 +62,14 @@ static void accepts_five_argument_badge(Badge<MultipleBadgeUserA, MultipleBadgeU
{
}
static void accepts_base_badge(Badge<BaseBadgeUser>)
{
}
static void accepts_multi_base_badge(Badge<BaseBadgeUser, MultipleBadgeUserB>)
{
}
void MultipleBadgeUserA::call_two_argument_badge()
{
accepts_two_argument_badge({});
@ -92,6 +110,26 @@ void MultipleBadgeUserE::call_five_argument_badge()
accepts_five_argument_badge({});
}
void BaseBadgeUser::call_base_badge()
{
accepts_base_badge({});
}
void BaseBadgeUser::call_multi_base_badge()
{
accepts_multi_base_badge({});
}
void DerivedBadgeUser::call_base_badge()
{
accepts_base_badge(Badge<DerivedBadgeUser> {});
}
void DerivedBadgeUser::call_multi_base_badge()
{
accepts_multi_base_badge(Badge<DerivedBadgeUser> {});
}
}
TEST_CASE(should_provide_underlying_type)
@ -104,11 +142,13 @@ TEST_CASE(should_allow_multiple_underlying_types)
static_assert(!IsConstructible<Badge<MultipleBadgeUserA, MultipleBadgeUserB>>);
static_assert(!IsConstructible<Badge<MultipleBadgeUserA, MultipleBadgeUserB, MultipleBadgeUserC>>);
static_assert(!IsConstructible<Badge<MultipleBadgeUserA, MultipleBadgeUserB, MultipleBadgeUserC, MultipleBadgeUserD>>);
static_assert(!IsConstructible<Badge<MultipleBadgeUserA, MultipleBadgeUserB>, Badge<MultipleBadgeUserA>>);
static_assert(!IsConstructible<Badge<MultipleBadgeUserA, MultipleBadgeUserB, MultipleBadgeUserC, MultipleBadgeUserD, MultipleBadgeUserE>>);
static_assert(!IsConstructible<
static_assert(IsConstructible<
Badge<MultipleBadgeUserA, MultipleBadgeUserB, MultipleBadgeUserC, MultipleBadgeUserD, MultipleBadgeUserE>,
Badge<MultipleBadgeUserA> const&>);
static_assert(IsConstructible<
Badge<MultipleBadgeUserA, MultipleBadgeUserB>,
Badge<MultipleBadgeUserA> const&>);
static_assert(!IsConstructible<Badge<MultipleBadgeUserA, MultipleBadgeUserB>, Badge<UnrelatedBadgeUser>>);
MultipleBadgeUserA::call_two_argument_badge();
@ -120,3 +160,33 @@ TEST_CASE(should_allow_multiple_underlying_types)
MultipleBadgeUserD::call_four_argument_badge();
MultipleBadgeUserE::call_five_argument_badge();
}
TEST_CASE(should_allow_derived_types_to_create_base_badges)
{
static_assert(IsConstructible<
Badge<BaseBadgeUser>,
Badge<DerivedBadgeUser> const&>);
static_assert(IsConstructible<
Badge<BaseBadgeUser, MultipleBadgeUserB>,
Badge<BaseBadgeUser> const&>);
static_assert(IsConstructible<
Badge<BaseBadgeUser, MultipleBadgeUserB>,
Badge<DerivedBadgeUser> const&>);
static_assert(IsConstructible<
Badge<BaseBadgeUser, MultipleBadgeUserB, MultipleBadgeUserC, MultipleBadgeUserD, MultipleBadgeUserE>,
Badge<DerivedBadgeUser> const&>);
static_assert(!IsConstructible<
Badge<DerivedBadgeUser>,
Badge<BaseBadgeUser> const&>);
static_assert(!IsConstructible<
Badge<BaseBadgeUser>,
Badge<BaseBadgeUser, MultipleBadgeUserB> const&>);
static_assert(!IsConstructible<
Badge<BaseBadgeUser>,
Badge<UnrelatedBadgeUser> const&>);
BaseBadgeUser::call_base_badge();
BaseBadgeUser::call_multi_base_badge();
DerivedBadgeUser::call_base_badge();
DerivedBadgeUser::call_multi_base_badge();
}