1*6777b538SAndroid Build Coastguard Worker // Copyright 2018 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #ifndef BASE_TRAITS_BAG_H_
6*6777b538SAndroid Build Coastguard Worker #define BASE_TRAITS_BAG_H_
7*6777b538SAndroid Build Coastguard Worker
8*6777b538SAndroid Build Coastguard Worker #include <initializer_list>
9*6777b538SAndroid Build Coastguard Worker #include <optional>
10*6777b538SAndroid Build Coastguard Worker #include <tuple>
11*6777b538SAndroid Build Coastguard Worker #include <type_traits>
12*6777b538SAndroid Build Coastguard Worker #include <utility>
13*6777b538SAndroid Build Coastguard Worker
14*6777b538SAndroid Build Coastguard Worker #include "base/parameter_pack.h"
15*6777b538SAndroid Build Coastguard Worker
16*6777b538SAndroid Build Coastguard Worker // A bag of Traits (structs / enums / etc...) can be an elegant alternative to
17*6777b538SAndroid Build Coastguard Worker // the builder pattern and multiple default arguments for configuring things.
18*6777b538SAndroid Build Coastguard Worker // Traits are terser than the builder pattern and can be evaluated at compile
19*6777b538SAndroid Build Coastguard Worker // time, however they require the use of variadic templates which complicates
20*6777b538SAndroid Build Coastguard Worker // matters. This file contains helpers that make Traits easier to use.
21*6777b538SAndroid Build Coastguard Worker //
22*6777b538SAndroid Build Coastguard Worker // WARNING: Trait bags are currently too heavy for non-constexpr usage in prod
23*6777b538SAndroid Build Coastguard Worker // code due to template bloat, although adding NOINLINE to template constructors
24*6777b538SAndroid Build Coastguard Worker // configured via trait bags can help.
25*6777b538SAndroid Build Coastguard Worker //
26*6777b538SAndroid Build Coastguard Worker // E.g.
27*6777b538SAndroid Build Coastguard Worker // struct EnableFeatureX {};
28*6777b538SAndroid Build Coastguard Worker // struct UnusedTrait {};
29*6777b538SAndroid Build Coastguard Worker // enum Color { RED, BLUE };
30*6777b538SAndroid Build Coastguard Worker //
31*6777b538SAndroid Build Coastguard Worker // struct ValidTraits {
32*6777b538SAndroid Build Coastguard Worker // ValidTraits(EnableFeatureX);
33*6777b538SAndroid Build Coastguard Worker // ValidTraits(Color);
34*6777b538SAndroid Build Coastguard Worker // };
35*6777b538SAndroid Build Coastguard Worker // ...
36*6777b538SAndroid Build Coastguard Worker // DoSomethingAwesome(); // Use defaults (Color::BLUE &
37*6777b538SAndroid Build Coastguard Worker // // feature X not enabled)
38*6777b538SAndroid Build Coastguard Worker // DoSomethingAwesome(EnableFeatureX(), // Turn feature X on
39*6777b538SAndroid Build Coastguard Worker // Color::RED); // And make it red.
40*6777b538SAndroid Build Coastguard Worker // DoSomethingAwesome(UnusedTrait(), // Compile time error.
41*6777b538SAndroid Build Coastguard Worker // Color::RED);
42*6777b538SAndroid Build Coastguard Worker //
43*6777b538SAndroid Build Coastguard Worker // DoSomethingAwesome might be defined as:
44*6777b538SAndroid Build Coastguard Worker //
45*6777b538SAndroid Build Coastguard Worker // template <class... ArgTypes>
46*6777b538SAndroid Build Coastguard Worker // requires trait_helpers::AreValidTraits<ValidTraits, ArgTypes...>
47*6777b538SAndroid Build Coastguard Worker // constexpr void DoSomethingAwesome(ArgTypes... args)
48*6777b538SAndroid Build Coastguard Worker // : enable_feature_x(
49*6777b538SAndroid Build Coastguard Worker // trait_helpers::HasTrait<EnableFeatureX, ArgTypes...>()),
50*6777b538SAndroid Build Coastguard Worker // color(trait_helpers::GetEnum<Color, EnumTraitA::BLUE>(args...)) {}
51*6777b538SAndroid Build Coastguard Worker
52*6777b538SAndroid Build Coastguard Worker namespace base {
53*6777b538SAndroid Build Coastguard Worker namespace trait_helpers {
54*6777b538SAndroid Build Coastguard Worker
55*6777b538SAndroid Build Coastguard Worker // Represents a trait that has been removed by a predicate.
56*6777b538SAndroid Build Coastguard Worker struct EmptyTrait {};
57*6777b538SAndroid Build Coastguard Worker
58*6777b538SAndroid Build Coastguard Worker // Predicate used to remove any traits from the given list of types by
59*6777b538SAndroid Build Coastguard Worker // converting them to EmptyTrait. E.g.
60*6777b538SAndroid Build Coastguard Worker //
61*6777b538SAndroid Build Coastguard Worker // template <typename... Args>
62*6777b538SAndroid Build Coastguard Worker // void MyFunc(Args... args) {
63*6777b538SAndroid Build Coastguard Worker // DoSomethingWithTraits(
64*6777b538SAndroid Build Coastguard Worker // base::trait_helpers::Exclude<UnwantedTrait1,
65*6777b538SAndroid Build Coastguard Worker // UnwantedTrait2>::Filter(args)...);
66*6777b538SAndroid Build Coastguard Worker // }
67*6777b538SAndroid Build Coastguard Worker //
68*6777b538SAndroid Build Coastguard Worker // NB It's possible to actually remove the unwanted trait from the pack, but
69*6777b538SAndroid Build Coastguard Worker // that requires constructing a filtered tuple and applying it to the function,
70*6777b538SAndroid Build Coastguard Worker // which isn't worth the complexity over ignoring EmptyTrait.
71*6777b538SAndroid Build Coastguard Worker template <typename... TraitsToExclude>
72*6777b538SAndroid Build Coastguard Worker struct Exclude {
73*6777b538SAndroid Build Coastguard Worker template <typename T>
FilterExclude74*6777b538SAndroid Build Coastguard Worker static constexpr auto Filter(T t) {
75*6777b538SAndroid Build Coastguard Worker if constexpr (ParameterPack<TraitsToExclude...>::template HasType<
76*6777b538SAndroid Build Coastguard Worker T>::value) {
77*6777b538SAndroid Build Coastguard Worker return EmptyTrait();
78*6777b538SAndroid Build Coastguard Worker } else {
79*6777b538SAndroid Build Coastguard Worker return t;
80*6777b538SAndroid Build Coastguard Worker }
81*6777b538SAndroid Build Coastguard Worker }
82*6777b538SAndroid Build Coastguard Worker };
83*6777b538SAndroid Build Coastguard Worker
84*6777b538SAndroid Build Coastguard Worker // CallFirstTag is an argument tag that helps to avoid ambiguous overloaded
85*6777b538SAndroid Build Coastguard Worker // functions. When the following call is made:
86*6777b538SAndroid Build Coastguard Worker // func(CallFirstTag(), arg...);
87*6777b538SAndroid Build Coastguard Worker // the compiler will give precedence to an overload candidate that directly
88*6777b538SAndroid Build Coastguard Worker // takes CallFirstTag. Another overload that takes CallSecondTag will be
89*6777b538SAndroid Build Coastguard Worker // considered iff the preferred overload candidates were all invalids and
90*6777b538SAndroid Build Coastguard Worker // therefore discarded.
91*6777b538SAndroid Build Coastguard Worker struct CallSecondTag {};
92*6777b538SAndroid Build Coastguard Worker struct CallFirstTag : CallSecondTag {};
93*6777b538SAndroid Build Coastguard Worker
94*6777b538SAndroid Build Coastguard Worker // A trait filter class |TraitFilterType| implements the protocol to get a value
95*6777b538SAndroid Build Coastguard Worker // of type |ArgType| from an argument list and convert it to a value of type
96*6777b538SAndroid Build Coastguard Worker // |TraitType|. If the argument list contains an argument of type |ArgType|, the
97*6777b538SAndroid Build Coastguard Worker // filter class will be instantiated with that argument. If the argument list
98*6777b538SAndroid Build Coastguard Worker // contains no argument of type |ArgType|, the filter class will be instantiated
99*6777b538SAndroid Build Coastguard Worker // using the default constructor if available; a compile error is issued
100*6777b538SAndroid Build Coastguard Worker // otherwise. The filter class must have the conversion operator TraitType()
101*6777b538SAndroid Build Coastguard Worker // which returns a value of type TraitType.
102*6777b538SAndroid Build Coastguard Worker
103*6777b538SAndroid Build Coastguard Worker // |InvalidTrait| is used to return from GetTraitFromArg when the argument is
104*6777b538SAndroid Build Coastguard Worker // not compatible with the desired trait.
105*6777b538SAndroid Build Coastguard Worker struct InvalidTrait {};
106*6777b538SAndroid Build Coastguard Worker
107*6777b538SAndroid Build Coastguard Worker // Returns an object of type |TraitFilterType| constructed from |arg| if
108*6777b538SAndroid Build Coastguard Worker // compatible, or |InvalidTrait| otherwise.
109*6777b538SAndroid Build Coastguard Worker template <class TraitFilterType, class ArgType>
110*6777b538SAndroid Build Coastguard Worker requires std::constructible_from<TraitFilterType, ArgType>
GetTraitFromArg(CallFirstTag,ArgType arg)111*6777b538SAndroid Build Coastguard Worker constexpr TraitFilterType GetTraitFromArg(CallFirstTag, ArgType arg) {
112*6777b538SAndroid Build Coastguard Worker return TraitFilterType(arg);
113*6777b538SAndroid Build Coastguard Worker }
114*6777b538SAndroid Build Coastguard Worker
115*6777b538SAndroid Build Coastguard Worker template <class TraitFilterType, class ArgType>
GetTraitFromArg(CallSecondTag,ArgType arg)116*6777b538SAndroid Build Coastguard Worker constexpr InvalidTrait GetTraitFromArg(CallSecondTag, ArgType arg) {
117*6777b538SAndroid Build Coastguard Worker return InvalidTrait();
118*6777b538SAndroid Build Coastguard Worker }
119*6777b538SAndroid Build Coastguard Worker
120*6777b538SAndroid Build Coastguard Worker // Returns an object of type |TraitFilterType| constructed from a compatible
121*6777b538SAndroid Build Coastguard Worker // argument in |args...|, or default constructed if none of the arguments are
122*6777b538SAndroid Build Coastguard Worker // compatible. This is the implementation of GetTraitFromArgList() with a
123*6777b538SAndroid Build Coastguard Worker // disambiguation tag.
124*6777b538SAndroid Build Coastguard Worker template <class TraitFilterType, class... ArgTypes>
125*6777b538SAndroid Build Coastguard Worker requires(std::constructible_from<TraitFilterType, ArgTypes> || ...)
GetTraitFromArgListImpl(CallFirstTag,ArgTypes...args)126*6777b538SAndroid Build Coastguard Worker constexpr TraitFilterType GetTraitFromArgListImpl(CallFirstTag,
127*6777b538SAndroid Build Coastguard Worker ArgTypes... args) {
128*6777b538SAndroid Build Coastguard Worker return std::get<TraitFilterType>(std::make_tuple(
129*6777b538SAndroid Build Coastguard Worker GetTraitFromArg<TraitFilterType>(CallFirstTag(), args)...));
130*6777b538SAndroid Build Coastguard Worker }
131*6777b538SAndroid Build Coastguard Worker
132*6777b538SAndroid Build Coastguard Worker template <class TraitFilterType, class... ArgTypes>
GetTraitFromArgListImpl(CallSecondTag,ArgTypes...args)133*6777b538SAndroid Build Coastguard Worker constexpr TraitFilterType GetTraitFromArgListImpl(CallSecondTag,
134*6777b538SAndroid Build Coastguard Worker ArgTypes... args) {
135*6777b538SAndroid Build Coastguard Worker static_assert(std::is_constructible_v<TraitFilterType>,
136*6777b538SAndroid Build Coastguard Worker "The traits bag is missing a required trait.");
137*6777b538SAndroid Build Coastguard Worker return TraitFilterType();
138*6777b538SAndroid Build Coastguard Worker }
139*6777b538SAndroid Build Coastguard Worker
140*6777b538SAndroid Build Coastguard Worker // Constructs an object of type |TraitFilterType| from a compatible argument in
141*6777b538SAndroid Build Coastguard Worker // |args...|, or using the default constructor, and returns its associated trait
142*6777b538SAndroid Build Coastguard Worker // value using conversion to |TraitFilterType::ValueType|. If there are more
143*6777b538SAndroid Build Coastguard Worker // than one compatible argument in |args|, generates a compile-time error.
144*6777b538SAndroid Build Coastguard Worker template <class TraitFilterType, class... ArgTypes>
GetTraitFromArgList(ArgTypes...args)145*6777b538SAndroid Build Coastguard Worker constexpr typename TraitFilterType::ValueType GetTraitFromArgList(
146*6777b538SAndroid Build Coastguard Worker ArgTypes... args) {
147*6777b538SAndroid Build Coastguard Worker static_assert(
148*6777b538SAndroid Build Coastguard Worker count({std::is_constructible_v<TraitFilterType, ArgTypes>...}, true) <= 1,
149*6777b538SAndroid Build Coastguard Worker "The traits bag contains multiple traits of the same type.");
150*6777b538SAndroid Build Coastguard Worker return GetTraitFromArgListImpl<TraitFilterType>(CallFirstTag(), args...);
151*6777b538SAndroid Build Coastguard Worker }
152*6777b538SAndroid Build Coastguard Worker
153*6777b538SAndroid Build Coastguard Worker // Helper class to implemnent a |TraitFilterType|.
154*6777b538SAndroid Build Coastguard Worker template <typename T, typename _ValueType = T>
155*6777b538SAndroid Build Coastguard Worker struct BasicTraitFilter {
156*6777b538SAndroid Build Coastguard Worker using ValueType = _ValueType;
157*6777b538SAndroid Build Coastguard Worker
BasicTraitFilterBasicTraitFilter158*6777b538SAndroid Build Coastguard Worker constexpr BasicTraitFilter(ValueType v) : value(v) {}
159*6777b538SAndroid Build Coastguard Worker
ValueTypeBasicTraitFilter160*6777b538SAndroid Build Coastguard Worker constexpr operator ValueType() const { return value; }
161*6777b538SAndroid Build Coastguard Worker
162*6777b538SAndroid Build Coastguard Worker ValueType value = {};
163*6777b538SAndroid Build Coastguard Worker };
164*6777b538SAndroid Build Coastguard Worker
165*6777b538SAndroid Build Coastguard Worker template <typename ArgType, ArgType DefaultValue>
166*6777b538SAndroid Build Coastguard Worker struct EnumTraitFilter : public BasicTraitFilter<ArgType> {
EnumTraitFilterEnumTraitFilter167*6777b538SAndroid Build Coastguard Worker constexpr EnumTraitFilter() : BasicTraitFilter<ArgType>(DefaultValue) {}
EnumTraitFilterEnumTraitFilter168*6777b538SAndroid Build Coastguard Worker constexpr EnumTraitFilter(ArgType arg) : BasicTraitFilter<ArgType>(arg) {}
169*6777b538SAndroid Build Coastguard Worker };
170*6777b538SAndroid Build Coastguard Worker
171*6777b538SAndroid Build Coastguard Worker template <typename ArgType>
172*6777b538SAndroid Build Coastguard Worker struct OptionalEnumTraitFilter
173*6777b538SAndroid Build Coastguard Worker : public BasicTraitFilter<ArgType, std::optional<ArgType>> {
OptionalEnumTraitFilterOptionalEnumTraitFilter174*6777b538SAndroid Build Coastguard Worker constexpr OptionalEnumTraitFilter()
175*6777b538SAndroid Build Coastguard Worker : BasicTraitFilter<ArgType, std::optional<ArgType>>(std::nullopt) {}
OptionalEnumTraitFilterOptionalEnumTraitFilter176*6777b538SAndroid Build Coastguard Worker constexpr OptionalEnumTraitFilter(ArgType arg)
177*6777b538SAndroid Build Coastguard Worker : BasicTraitFilter<ArgType, std::optional<ArgType>>(arg) {}
178*6777b538SAndroid Build Coastguard Worker };
179*6777b538SAndroid Build Coastguard Worker
180*6777b538SAndroid Build Coastguard Worker // Tests whether multiple given argtument types are all valid traits according
181*6777b538SAndroid Build Coastguard Worker // to the provided ValidTraits. To use, define a ValidTraits
182*6777b538SAndroid Build Coastguard Worker template <typename ArgType>
183*6777b538SAndroid Build Coastguard Worker struct RequiredEnumTraitFilter : public BasicTraitFilter<ArgType> {
RequiredEnumTraitFilterRequiredEnumTraitFilter184*6777b538SAndroid Build Coastguard Worker constexpr RequiredEnumTraitFilter(ArgType arg)
185*6777b538SAndroid Build Coastguard Worker : BasicTraitFilter<ArgType>(arg) {}
186*6777b538SAndroid Build Coastguard Worker };
187*6777b538SAndroid Build Coastguard Worker
188*6777b538SAndroid Build Coastguard Worker // Note EmptyTrait is always regarded as valid to support filtering.
189*6777b538SAndroid Build Coastguard Worker template <class ValidTraits, class T>
190*6777b538SAndroid Build Coastguard Worker concept IsValidTrait =
191*6777b538SAndroid Build Coastguard Worker std::constructible_from<ValidTraits, T> || std::same_as<T, EmptyTrait>;
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker // Tests whether a given trait type is valid or invalid by testing whether it is
194*6777b538SAndroid Build Coastguard Worker // convertible to the provided ValidTraits type. To use, define a ValidTraits
195*6777b538SAndroid Build Coastguard Worker // type like this:
196*6777b538SAndroid Build Coastguard Worker //
197*6777b538SAndroid Build Coastguard Worker // struct ValidTraits {
198*6777b538SAndroid Build Coastguard Worker // ValidTraits(MyTrait);
199*6777b538SAndroid Build Coastguard Worker // ...
200*6777b538SAndroid Build Coastguard Worker // };
201*6777b538SAndroid Build Coastguard Worker //
202*6777b538SAndroid Build Coastguard Worker // You can 'inherit' valid traits like so:
203*6777b538SAndroid Build Coastguard Worker //
204*6777b538SAndroid Build Coastguard Worker // struct MoreValidTraits {
205*6777b538SAndroid Build Coastguard Worker // MoreValidTraits(ValidTraits); // Pull in traits from ValidTraits.
206*6777b538SAndroid Build Coastguard Worker // MoreValidTraits(MyOtherTrait);
207*6777b538SAndroid Build Coastguard Worker // ...
208*6777b538SAndroid Build Coastguard Worker // };
209*6777b538SAndroid Build Coastguard Worker template <class ValidTraits, class... ArgTypes>
210*6777b538SAndroid Build Coastguard Worker concept AreValidTraits = (IsValidTrait<ValidTraits, ArgTypes> && ...);
211*6777b538SAndroid Build Coastguard Worker
212*6777b538SAndroid Build Coastguard Worker // Helper to make getting an enum from a trait more readable.
213*6777b538SAndroid Build Coastguard Worker template <typename Enum, typename... Args>
GetEnum(Args...args)214*6777b538SAndroid Build Coastguard Worker static constexpr Enum GetEnum(Args... args) {
215*6777b538SAndroid Build Coastguard Worker return GetTraitFromArgList<RequiredEnumTraitFilter<Enum>>(args...);
216*6777b538SAndroid Build Coastguard Worker }
217*6777b538SAndroid Build Coastguard Worker
218*6777b538SAndroid Build Coastguard Worker // Helper to make getting an enum from a trait with a default more readable.
219*6777b538SAndroid Build Coastguard Worker template <typename Enum, Enum DefaultValue, typename... Args>
GetEnum(Args...args)220*6777b538SAndroid Build Coastguard Worker static constexpr Enum GetEnum(Args... args) {
221*6777b538SAndroid Build Coastguard Worker return GetTraitFromArgList<EnumTraitFilter<Enum, DefaultValue>>(args...);
222*6777b538SAndroid Build Coastguard Worker }
223*6777b538SAndroid Build Coastguard Worker
224*6777b538SAndroid Build Coastguard Worker // Helper to make getting an optional enum from a trait with a default more
225*6777b538SAndroid Build Coastguard Worker // readable.
226*6777b538SAndroid Build Coastguard Worker template <typename Enum, typename... Args>
GetOptionalEnum(Args...args)227*6777b538SAndroid Build Coastguard Worker static constexpr std::optional<Enum> GetOptionalEnum(Args... args) {
228*6777b538SAndroid Build Coastguard Worker return GetTraitFromArgList<OptionalEnumTraitFilter<Enum>>(args...);
229*6777b538SAndroid Build Coastguard Worker }
230*6777b538SAndroid Build Coastguard Worker
231*6777b538SAndroid Build Coastguard Worker // Helper to make checking for the presence of a trait more readable.
232*6777b538SAndroid Build Coastguard Worker template <typename Trait, typename... Args>
233*6777b538SAndroid Build Coastguard Worker struct HasTrait : ParameterPack<Args...>::template HasType<Trait> {
234*6777b538SAndroid Build Coastguard Worker static_assert(count({std::is_constructible_v<Trait, Args>...}, true) <= 1,
235*6777b538SAndroid Build Coastguard Worker "The traits bag contains multiple traits of the same type.");
236*6777b538SAndroid Build Coastguard Worker };
237*6777b538SAndroid Build Coastguard Worker
238*6777b538SAndroid Build Coastguard Worker // If you need a template vararg constructor to delegate to a private
239*6777b538SAndroid Build Coastguard Worker // constructor, you may need to add this to the private constructor to ensure
240*6777b538SAndroid Build Coastguard Worker // it's not matched by accident.
241*6777b538SAndroid Build Coastguard Worker struct NotATraitTag {};
242*6777b538SAndroid Build Coastguard Worker
243*6777b538SAndroid Build Coastguard Worker } // namespace trait_helpers
244*6777b538SAndroid Build Coastguard Worker } // namespace base
245*6777b538SAndroid Build Coastguard Worker
246*6777b538SAndroid Build Coastguard Worker #endif // BASE_TRAITS_BAG_H_
247