From 9007909d07a5019257ce8836d1592945f8cf99ca Mon Sep 17 00:00:00 2001 From: Thomas Nixon Date: Wed, 16 Nov 2022 16:15:01 +0000 Subject: [PATCH 1/9] auto base: rework for smaller code size - make Combine corecursive with HasParameters so that the tail stays encoded as a list of parameter classes, rather than the full inheritance structure - avoid using CombineRaw by adding more combination classes that inherit from two bases - avoid virtual base in variant parameter - add tests for variant parameters - move more of the details out of the main header - move tag types out of template parameters - turn flags into types to cut down on symbols --- include/adm/detail/auto_base.hpp | 179 ++++++------------ include/adm/detail/auto_base_detail.hpp | 235 ++++++++++++++++++++++-- tests/auto_base_tests.cpp | 86 ++++++--- 3 files changed, 334 insertions(+), 166 deletions(-) diff --git a/include/adm/detail/auto_base.hpp b/include/adm/detail/auto_base.hpp index d8d07262..a6583bc9 100644 --- a/include/adm/detail/auto_base.hpp +++ b/include/adm/detail/auto_base.hpp @@ -44,113 +44,56 @@ namespace adm { // chained, with the actual inheritance happening in CombineRaw. When // combining A and B we have an inheritance hierarchy like: // - // Combine - // -> ApplyIf, X> - // -> ApplyIf, Y> - // -> CombineRaw - // -> A - // -> B + // To enable all combinations without making the dreaded diamond, these are + // chained, with the actual inheritance happening in one of the Combine*B + // classes. When combining two or more classes we have an inheritance + // hierarchy like: // - // where X and Y are the flags controlling whether Combine* is applied or - // not. + // Combine { B = HasParameters } + // -> CombineGetSetHas + // -> CombineIsDefaultUnsetB + // -> A + // -> B // - // Combine is used by HasParameters, which recursively combines many *Base - // classes. + // Combine is used co-recursively by HasParameters, which combines many + // *Base classes. // // For variant types, there is one base class which implements the methods // for the variant type (e.g. OptionalParameter>), and one // base class for each of the types T in the variant, e.g. // VariantTypeParameter. // - // VariantTypeParameter classes must inherit from OptionalParameter in - // order to access the variant (through the get/set methods). This could be - // implemented by templating VariantTypeParameter on the list of types of - // the variant, and each inheriting from the next, but each of these - // classes is to be explicitly instantiated, and the meaning of these - // parameters in the explicit instantiations would not be clear. + // These are combined together using VariantParameter. This works similarly + // to HasParameters -- the VariantTypeParameter for the first type in the + // variant inherits from the VariantTypeParameter from the next, and so on, + // until the OptionalParameter>. This structure is necessary + // because the methids in VariantTypeParameter need to cahh the methods on + // the OptionalParameter>, so must inherit from it. + // + // For example, if we have P = OptionalParameter>, then + // VariantParameter is: // - // Instead, each VariantTypeParameter inherits from OptionalParameter, and - // virtual inheritance is used to avoid ambiguity. These are then combined - // together in VariantParameter using HasParameters, so that only one type - // has to be added to the base class list to cover the whole variant. + // VariantParameter

= VariantTypeParameter // - // For clarity, if we have P = OptionalParameter>, then the - // inheritance structure looks like: + // And the inheritance structure looks like: // - // VariantParameter

- // -> VariantTypeParameter - // -> P - // -> VariantTypeParameter - // -> P + // VariantTypeParameter + // -> VariantTypeParameter + // -> VariantTypeParameter + // -> P #ifndef IN_DOXYGEN struct Flags { - static constexpr bool has_get_set_has = false; - static constexpr bool has_isDefault_unset = false; - static constexpr bool has_add_remove = false; - }; - - /// a subclass of Base, with using declarations for set, get and has in A - /// and B - template - struct CombineGetSetHas : public Base { - using A::get; - using B::get; - - using A::set; - using B::set; - - using A::has; - using B::has; - }; - - /// a subclass of Base, with using declarations for isDefault and unset in A - /// and B - template - struct CombineIsDefaultUnset : public Base { - using A::isDefault; - using B::isDefault; - - using A::unset; - using B::unset; - }; - - /// a subclass of Base, with using declarations for add and remove in A and - /// B - template - struct CombineAddRemove : public Base { - using A::add; - using B::add; - - using A::remove; - using B::remove; - }; - - /// a subclass of A and B, with methods according to their Flags - template - struct Combine - : public ApplyIf< - A::has_add_remove && B::has_add_remove, CombineAddRemove, A, B, - ApplyIf>>> { - static constexpr bool has_get_set_has = - A::has_get_set_has || B::has_get_set_has; - static constexpr bool has_isDefault_unset = - A::has_isDefault_unset || B::has_isDefault_unset; - static constexpr bool has_add_remove = - A::has_add_remove || B::has_add_remove; + using has_get_set_has = std::false_type; + using has_isDefault_unset = std::false_type; + using has_add_remove = std::false_type; }; /// make a class derived from the given base classes, combining the /// get, set, has, isDefault and unset overloads - template - struct HasParameters : public Combine> {}; - - template - struct HasParameters : public B {}; + template + using HasParameters = typename HasParametersT::type; /// Get the default value of T parameters. Specialise this to add custom /// defaults. @@ -161,12 +104,13 @@ namespace adm { /// base class with set/get/has methods for a required parameter of type T /// combine these together using HasParameters - template ::tag> + template class RequiredParameter : public Flags { + using Tag = typename detail::ParameterTraits::tag; + public: using ParameterType = T; - static constexpr bool has_get_set_has = true; + using has_get_set_has = std::true_type; ADM_BASE_EXPORT T get(Tag) const { return value_; } ADM_BASE_EXPORT void set(T value) { value_ = std::move(value); } @@ -179,13 +123,14 @@ namespace adm { /// base class with set/get/has/isDefault/unset methods for an optional /// parameter of type T with no default. combine these together using /// HasParameters - template ::tag> + template class OptionalParameter : public Flags { + using Tag = typename detail::ParameterTraits::tag; + public: using ParameterType = T; - static constexpr bool has_get_set_has = true; - static constexpr bool has_isDefault_unset = true; + using has_get_set_has = std::true_type; + using has_isDefault_unset = std::true_type; ADM_BASE_EXPORT T get(Tag) const { return value_.get(); } ADM_BASE_EXPORT void set(T value) { value_ = std::move(value); } @@ -200,13 +145,14 @@ namespace adm { /// base class with set/get/has/isDefault/unset methods for an optional /// parameter of type T, which has a default provided by DefaultParameter. /// combine these together using HasParameters - template ::tag> + template class DefaultParameter : public Flags { + using Tag = typename detail::ParameterTraits::tag; + public: using ParameterType = T; - static constexpr bool has_get_set_has = true; - static constexpr bool has_isDefault_unset = true; + using has_get_set_has = std::true_type; + using has_isDefault_unset = std::true_type; ADM_BASE_EXPORT T get(Tag) const { return boost::get_optional_value_or(value_, getDefault()); @@ -230,16 +176,16 @@ namespace adm { /// base class for storage of multiple elements in a std::vector. /// T should be a std::vector, as this is what the tag is /// associated with. - template ::tag> + template class VectorParameter : public Flags { using Value = typename T::value_type; + using Tag = typename detail::ParameterTraits::tag; public: using ParameterType = T; - static constexpr bool has_get_set_has = true; - static constexpr bool has_isDefault_unset = true; - static constexpr bool has_add_remove = true; + using has_get_set_has = std::true_type; + using has_isDefault_unset = std::true_type; + using has_add_remove = std::true_type; ADM_BASE_EXPORT T get(Tag) const { return value_; } ADM_BASE_EXPORT void set(T value) { value_ = std::move(value); } @@ -278,46 +224,39 @@ namespace adm { /// OptionalParameter>, and T should be one of the /// types of the variant. template - class VariantTypeParameter : public virtual VariantParam { + class VariantTypeParameter + : public VariantTypeParameterBase { using Variant = typename VariantParam::ParameterType; using Tag = typename detail::ParameterTraits::tag; using VariantTag = typename detail::ParameterTraits::tag; public: - using VariantParam::get; + using VariantTypeParameterBase::get; ADM_BASE_EXPORT T get(Tag) const { return boost::get(get(VariantTag{})); } - using VariantParam::set; + using VariantTypeParameterBase::set; ADM_BASE_EXPORT void set(T value) { return VariantParam::set(std::move(value)); } - using VariantParam::has; + using VariantTypeParameterBase::has; ADM_BASE_EXPORT bool has(Tag) const { return has(VariantTag{}) && get(VariantTag()).type() == typeid(T); } - using VariantParam::isDefault; + using VariantTypeParameterBase::isDefault; ADM_BASE_EXPORT bool isDefault(Tag) const { return isDefault(VariantTag()) && get(VariantTag()).type() == typeid(T); } - using VariantParam::unset; + using VariantTypeParameterBase::unset; ADM_BASE_EXPORT void unset(Tag) { if (has(Tag{})) unset(VariantTag{}); } }; - template - struct VariantParameterHelper; - - template - struct VariantParameterHelper> { - using type = HasParameters...>; - }; - /// Wrapper which has methods for each type in a variant parameter. /// /// This should be used in HasParameters, with VariantParam being a @@ -328,8 +267,8 @@ namespace adm { /// - OptionalParameter (not VariantParameter<...>) /// - One VariantParameter, T> for each T in V. template - using VariantParameter = typename VariantParameterHelper< - VariantParam, typename VariantParam::ParameterType>::type; + using VariantParameter = VariantTypeParameter< + VariantParam, VariantFirstType>; /// Helper containing templated wrapper methods like `has()` around /// overloaded `has(ParamTag)` type methods defined in T. diff --git a/include/adm/detail/auto_base_detail.hpp b/include/adm/detail/auto_base_detail.hpp index 0d9d94ed..24e320b5 100644 --- a/include/adm/detail/auto_base_detail.hpp +++ b/include/adm/detail/auto_base_detail.hpp @@ -1,31 +1,230 @@ +#include "boost/variant.hpp" namespace adm { namespace detail { - /// Combine A and B using F is defined_in_both - template class F, - typename A, typename B, typename Base> - struct ApplyIfT; + /// get the first type parameter of a variant + template + struct VariantFirstTypeT; - template