diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ec58f52..96f86596 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.8) ############################################################ # set global paths diff --git a/include/adm/detail/auto_base.hpp b/include/adm/detail/auto_base.hpp index d8d07262..310e7972 100644 --- a/include/adm/detail/auto_base.hpp +++ b/include/adm/detail/auto_base.hpp @@ -27,130 +27,12 @@ namespace adm { // This is done using multiple inheritance: given a set of *Base classes, // we can make a derived class using HasParameters which inherits from all // of them. - // - // The problem this presents is that the derived class needs a using - // declaration pointing to each method in the base classes, in order to - // bring them into one overload set, but we can't have a using declaration - // which points to a method which doesn't exist. - // - // To make this work, each *Base class defines some flags (defined in - // Flags) which says which sets of methods it implements. When Combine is - // used to combine two *Base classes together, these flags are or-ed in the - // resulting class, and the correct specialisations of the Combine* classes - // are selected by and-ing the flags (we only need the using declarations - // if both bases have the methods defined). - // - // To enable all combinations without making the dreaded diamond, these are - // 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 - // - // where X and Y are the flags controlling whether Combine* is applied or - // not. - // - // Combine is used by HasParameters, which recursively 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. - // - // 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. - // - // For clarity, if we have P = OptionalParameter>, then the - // inheritance structure looks like: - // - // VariantParameter

- // -> VariantTypeParameter - // -> P - // -> 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; - }; - /// 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 = Combine>; /// Get the default value of T parameters. Specialise this to add custom /// defaults. @@ -161,12 +43,13 @@ namespace adm { /// base class with set/get/has methods for a required parameter of type T /// combine these together using HasParameters - template ::tag> - class RequiredParameter : public Flags { + template + class RequiredParameter { + using Tag = typename detail::ParameterTraits::tag; + public: using ParameterType = T; - static constexpr bool has_get_set_has = true; + static constexpr Flags flags = Flags::HAS_GET_SET_HAS; ADM_BASE_EXPORT T get(Tag) const { return value_; } ADM_BASE_EXPORT void set(T value) { value_ = std::move(value); } @@ -179,13 +62,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> - class OptionalParameter : public Flags { + template + class OptionalParameter { + 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 Flags flags = + Flags::HAS_GET_SET_HAS | Flags::HAS_ISDEFAULT_UNSET; ADM_BASE_EXPORT T get(Tag) const { return value_.get(); } ADM_BASE_EXPORT void set(T value) { value_ = std::move(value); } @@ -200,13 +84,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> - class DefaultParameter : public Flags { + template + class DefaultParameter { + 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 Flags flags = + Flags::HAS_GET_SET_HAS | Flags::HAS_ISDEFAULT_UNSET; ADM_BASE_EXPORT T get(Tag) const { return boost::get_optional_value_or(value_, getDefault()); @@ -230,16 +115,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> - class VectorParameter : public Flags { + template + class VectorParameter { + using Tag = typename detail::ParameterTraits::tag; using Value = typename T::value_type; 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; + static constexpr Flags flags = Flags::HAS_GET_SET_HAS | + Flags::HAS_ISDEFAULT_UNSET | + Flags::HAS_ADD_REMOVE; ADM_BASE_EXPORT T get(Tag) const { return value_; } ADM_BASE_EXPORT void set(T value) { value_ = std::move(value); } @@ -272,64 +157,83 @@ namespace adm { } }; + /// base class which has methods for each type in a variant parameter. + /// + /// This should be used in HasParameters, with Parameter being a + /// parameter like OptionalParameter, where V is a boost::variant. + /// + /// When using this with OptionalParameter, the following classes should + /// be explicitly instantiated: + /// - OptionalParameter + /// - One VariantTypeParameter, T> for each T in V. + /// - VariantParameter> + template + class VariantParameter; + /// Base class for one type within a variant. /// - /// VariantParam should be a parameter type like + /// Parameter should be a parameter type like /// OptionalParameter>, and T should be one of the /// types of the variant. - template - class VariantTypeParameter : public virtual VariantParam { - using Variant = typename VariantParam::ParameterType; + /// + /// this uses CRTP with VariantParameter to access the Variant type + template + class VariantTypeParameter { + using Base = VariantParameter; using Tag = typename detail::ParameterTraits::tag; - using VariantTag = typename detail::ParameterTraits::tag; + + Base& base() { return static_cast(*this); } + + const Base& base() const { return static_cast(*this); } public: - using VariantParam::get; ADM_BASE_EXPORT T get(Tag) const { - return boost::get(get(VariantTag{})); + return boost::get(base().get(typename Base::VariantTag{})); } - using VariantParam::set; ADM_BASE_EXPORT void set(T value) { - return VariantParam::set(std::move(value)); + return base().set(typename Base::Variant{std::move(value)}); } - using VariantParam::has; ADM_BASE_EXPORT bool has(Tag) const { - return has(VariantTag{}) && get(VariantTag()).type() == typeid(T); + return base().has(typename Base::VariantTag{}) && + base().get(typename Base::VariantTag{}).type() == typeid(T); } - using VariantParam::isDefault; ADM_BASE_EXPORT bool isDefault(Tag) const { - return isDefault(VariantTag()) && get(VariantTag()).type() == typeid(T); + return base().isDefault(typename Base::VariantTag{}) && + base().get(typename Base::VariantTag{}).type() == typeid(T); } - using VariantParam::unset; ADM_BASE_EXPORT void unset(Tag) { - if (has(Tag{})) unset(VariantTag{}); + if (has(Tag{})) base().unset(typename Base::VariantTag{}); } }; - template - struct VariantParameterHelper; + // implementation of VariantParameter; see above + template + class VariantParameter> + : public Parameter, public VariantTypeParameter... { + public: + using Variant = boost::variant; + using VariantTag = typename detail::ParameterTraits::tag; - template - struct VariantParameterHelper> { - using type = HasParameters...>; - }; + using Parameter::get; + using VariantTypeParameter::get...; - /// Wrapper which has methods for each type in a variant parameter. - /// - /// This should be used in HasParameters, with VariantParam being a - /// parameter like OptionalParameter, where V is a boost::variant. - /// - /// When using this with OptionalParameter, the following classes should - /// be explicitly instantiated: - /// - OptionalParameter (not VariantParameter<...>) - /// - One VariantParameter, T> for each T in V. - template - using VariantParameter = typename VariantParameterHelper< - VariantParam, typename VariantParam::ParameterType>::type; + using Parameter::set; + using VariantTypeParameter::set...; + + using Parameter::has; + using VariantTypeParameter::has...; + + using Parameter::isDefault; + using VariantTypeParameter::isDefault...; + + using Parameter::unset; + using VariantTypeParameter::unset...; + }; /// 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 a28fb2b6..e78bcb53 100644 --- a/include/adm/detail/auto_base_detail.hpp +++ b/include/adm/detail/auto_base_detail.hpp @@ -1,31 +1,105 @@ +#pragma once + +#include "adm/detail/enum_bitmask.hpp" +#include namespace adm { namespace detail { - /// Combine A and B using F if defined_in_both, otherwise Base - template class F, - typename A, typename B, typename Base> - struct ApplyIfT; - - template