diff --git a/dumux/common/properties/newpropertysystem.hh b/dumux/common/properties/newpropertysystem.hh new file mode 100644 index 0000000000000000000000000000000000000000..cfed97a9bbfb0ce5b291c2d4fc88a319b3610a4e --- /dev/null +++ b/dumux/common/properties/newpropertysystem.hh @@ -0,0 +1,130 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \ingroup Common + * \ingroup TypeTraits + * \author Timo Koch + * \brief The Dumux property system, traits with inheritance + */ +#ifndef DUMUX_NEW_PROPERTY_SYSTEM_HH +#define DUMUX_NEW_PROPERTY_SYSTEM_HH + +#include <tuple> +#include <type_traits> + +namespace Dumux { +namespace Properties { + +//! a tag to mark properties as undefined +struct UndefinedProperty {}; + +//! implementation details for template meta programming +namespace Detail { + +//! check if a property P is defined +template<class P> +constexpr auto isDefinedProperty(int) +-> decltype(std::integral_constant<bool, !std::is_same<typename P::type, UndefinedProperty>::value>{}) +{ return {}; } + +//! fall back if a Property is defined +template<class P> +constexpr std::true_type isDefinedProperty(...) { return {}; } + +//! check if a TypeTag inherits from other TypeTags +template<class T> +constexpr auto hasParentTypeTag(int) +-> decltype(std::declval<typename T::InheritsFrom>(), std::true_type{}) +{ return {}; } + +//! fall back if a TypeTag doesn't inherit +template<class T> +constexpr std::false_type hasParentTypeTag(...) { return {}; } + +//! helper alias to concatenate multiple tuples +template<class ...Tuples> +using ConCatTuples = decltype(std::tuple_cat(std::declval<Tuples>()...)); + +//! helper struct to get the first property that is defined in the TypeTag hierarchy +template<class TypeTag, template<class,class> class Property, class TTagList> +struct GetDefined; + +//! helper struct to iteratre over the TypeTag hierarchy +template<class TypeTag, template<class,class> class Property, class TTagList, class Enable> +struct GetNextTypeTag; + +template<class TypeTag, template<class,class> class Property, class LastTypeTag> +struct GetNextTypeTag<TypeTag, Property, std::tuple<LastTypeTag>, std::enable_if_t<hasParentTypeTag<LastTypeTag>(int{}), void>> +{ using type = typename GetDefined<TypeTag, Property, typename LastTypeTag::InheritsFrom>::type; }; + +template<class TypeTag, template<class,class> class Property, class LastTypeTag> +struct GetNextTypeTag<TypeTag, Property, std::tuple<LastTypeTag>, std::enable_if_t<!hasParentTypeTag<LastTypeTag>(int{}), void>> +{ using type = UndefinedProperty; }; + +template<class TypeTag, template<class,class> class Property, class FirstTypeTag, class ...Args> +struct GetNextTypeTag<TypeTag, Property, std::tuple<FirstTypeTag, Args...>, std::enable_if_t<hasParentTypeTag<FirstTypeTag>(int{}), void>> +{ using type = typename GetDefined<TypeTag, Property, ConCatTuples<typename FirstTypeTag::InheritsFrom, std::tuple<Args...>>>::type; }; + +template<class TypeTag, template<class,class> class Property, class FirstTypeTag, class ...Args> +struct GetNextTypeTag<TypeTag, Property, std::tuple<FirstTypeTag, Args...>, std::enable_if_t<!hasParentTypeTag<FirstTypeTag>(int{}), void>> +{ using type = typename GetDefined<TypeTag, Property, std::tuple<Args...>>::type; }; + +template<class TypeTag, template<class,class> class Property, class LastTypeTag> +struct GetDefined<TypeTag, Property, std::tuple<LastTypeTag>> +{ + using LastType = Property<TypeTag, LastTypeTag>; + using type = std::conditional_t<isDefinedProperty<LastType>(int{}), LastType, + typename GetNextTypeTag<TypeTag, Property, std::tuple<LastTypeTag>, void>::type>; +}; + +template<class TypeTag, template<class,class> class Property, class FirstTypeTag, class ...Args> +struct GetDefined<TypeTag, Property, std::tuple<FirstTypeTag, Args...>> +{ + using FirstType = Property<TypeTag, FirstTypeTag>; + using type = std::conditional_t<isDefinedProperty<FirstType>(int{}), FirstType, + typename GetNextTypeTag<TypeTag, Property, std::tuple<FirstTypeTag, Args...>, void>::type>; +}; + +//! helper struct to extract get the Property specilization given a TypeTag, asserts that the property is defined +template<class TypeTag, template<class,class> class Property> +struct GetPropImpl +{ + using type = typename Detail::GetDefined<TypeTag, Property, std::tuple<TypeTag>>::type; + static_assert(!std::is_same<type, UndefinedProperty>::value, "Property is undefined!"); +}; + +} // end namespace Detail + +//! get the type of a property (equivalent to old macro GET_PROP(...)) +template<class TypeTag, template<class,class> class Property> +using GetProp = typename Detail::GetPropImpl<TypeTag, Property>::type; + +//! get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(...)) +template<class TypeTag, template<class,class> class Property> +using GetPropType = typename Detail::GetPropImpl<TypeTag, Property>::type::type; + +//! get the value data member of a property (C++17 only, equivalent to old macro GET_PROP_VALUE(...)) +// template<class TypeTag, template<class,class> class Property> +// constexpr auto getPropValue = Detail::GetPropImpl<TypeTag, Property>::type::value; + +} // end namespace Property +} // end namespace Dumux + +#endif diff --git a/test/common/propertysystem/CMakeLists.txt b/test/common/propertysystem/CMakeLists.txt index a5eeb1c9721bc4ab386292027deb184a2fac0bc8..65a70edc5753cfe8ae0b4ceee08b344e77c245a6 100644 --- a/test/common/propertysystem/CMakeLists.txt +++ b/test/common/propertysystem/CMakeLists.txt @@ -1,5 +1,7 @@ # build the test for the property system dune_add_test(SOURCES test_propertysystem.cc) +# build the test for the new property system +dune_add_test(SOURCES test_newpropertysystem.cc) #install sources install(FILES diff --git a/test/common/propertysystem/test_newpropertysystem.cc b/test/common/propertysystem/test_newpropertysystem.cc new file mode 100644 index 0000000000000000000000000000000000000000..56515ea3551cbfd9701f87533e33b8685cba0740 --- /dev/null +++ b/test/common/propertysystem/test_newpropertysystem.cc @@ -0,0 +1,124 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/***************************************************************************** + * See the file COPYING for full copying permissions. * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *****************************************************************************/ +/*! + * \file + * \ingroup Common + * \ingroup Tests + * \brief Testing the Dumux property system + */ + +#include <iostream> +#include <type_traits> + +#include <dune/common/classname.hh> +#include <dune/common/exceptions.hh> + +#include <dumux/common/properties/newpropertysystem.hh> + +namespace Dumux { +namespace Properties { + +// create some Properties (equivalent to old macro NEW_PROP_TAG(...)) +// the first type tag is the actual TypeTag for which the property will be obtained +// (can be used to make properties depend on other properties), +// the second type tag is for parital specialization (equivalent to old macro SET_PROP(...), see below) +// the default property should be always undefined to produce a good error message +// if the user attempt to get an unset property +template<class TypeTag, class MyTypeTag> +struct Scalar { using type = UndefinedProperty; }; + +template<class TypeTag, class MyTypeTag> +struct CoordinateType { using type = UndefinedProperty; }; + +namespace TTag { +// create some TypeTags (equivalent to old macro NEW_TYPE_TAG(..., INHERITS_FROM(...))) +// the tuple is sorted by precedence, the first one overwriting the following +struct Base { }; +struct Grid { }; +struct CCTpfaDisc { using InheritsFrom = std::tuple<Grid, Base>; }; +struct BoxDisc { using InheritsFrom = std::tuple<Grid, Base>; }; +struct OnePModel { using InheritsFrom = std::tuple<Base>; }; +struct OnePTestTypeTag { using InheritsFrom = std::tuple<OnePModel, BoxDisc>; }; + +} // end namespace TTag + +// set and overwrite some properties (equivalent to old macro SET_PROP(...){};) +template<class TypeTag> +struct Scalar<TypeTag, TTag::Base> { using type = float; }; + +template<class TypeTag> +struct Scalar<TypeTag, TTag::OnePModel> { using type = double; }; + +template<class TypeTag> +struct Scalar<TypeTag, TTag::OnePTestTypeTag> { using type = int; }; + +template<class TypeTag> +struct CoordinateType<TypeTag, TTag::Grid> { using type = GetPropType<TypeTag, Scalar>; }; + +} // end namespace Properties +} // end namespace Dumux + +//! the main function +int main(int argc, char* argv[]) try +{ + using namespace Dumux; + using namespace Properties; + + { + using Scalar = GetPropType<TTag::Base, Scalar>; + if (!std::is_same<Scalar, float>::value) + DUNE_THROW(Dune::InvalidStateException, "Property Scalar in TTag::Base should be float but is " << Dune::className<Scalar>()); + } + { + using Scalar = GetPropType<TTag::OnePTestTypeTag, Scalar>; + if (!std::is_same<Scalar, int>::value) + DUNE_THROW(Dune::InvalidStateException, "Property Scalar in TTag::OnePTestTypeTag should be int but is " << Dune::className<Scalar>()); + } + { + using Scalar = GetPropType<TTag::OnePModel, Scalar>; + if (!std::is_same<Scalar, double>::value) + DUNE_THROW(Dune::InvalidStateException, "Property Scalar in TTag::OnePModel should be double but is " << Dune::className<Scalar>()); + } + { + using CoordinateType = GetPropType<TTag::OnePTestTypeTag, CoordinateType>; + if (!std::is_same<CoordinateType, int>::value) + DUNE_THROW(Dune::InvalidStateException, "Property CoordinateType in TTag::OnePTestTypeTag should be int but is " << Dune::className<CoordinateType>()); + } + { + using CoordinateType = GetPropType<TTag::CCTpfaDisc, CoordinateType>; + if (!std::is_same<CoordinateType, float>::value) + DUNE_THROW(Dune::InvalidStateException, "Property CoordinateType in TTag::CCTpfaDisc should be float but is " << Dune::className<CoordinateType>()); + } + + std::cout << "All tests passed!" << std::endl; + return 0; +} + +// error handler +catch (const Dune::Exception& e) +{ + std::cerr << "Dune exception thrown: " << e << " --> Abort!" << std::endl; + return 1; +} + +catch (...) +{ + std::cerr << "Unknown exception thrown --> Abort!" << std::endl; + return 2; +}