diff --git a/dumux/common/typetraits/CMakeLists.txt b/dumux/common/typetraits/CMakeLists.txt index 41bd98a128d5fd2838094159a187e34828d5dc4a..f71dd84a50c7ae4e60327b1ecb0ea273ebb15675 100644 --- a/dumux/common/typetraits/CMakeLists.txt +++ b/dumux/common/typetraits/CMakeLists.txt @@ -2,4 +2,5 @@ install(FILES matrix.hh vector.hh +isvalid.hh DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/common/typetraits) diff --git a/dumux/common/typetraits/isvalid.hh b/dumux/common/typetraits/isvalid.hh new file mode 100644 index 0000000000000000000000000000000000000000..4be5daa17fd4137c9ef3d9a6a6ec09cbdde8c997 --- /dev/null +++ b/dumux/common/typetraits/isvalid.hh @@ -0,0 +1,99 @@ +// -*- 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 + * \brief A helper function for class member function introspection + * \note Follows the description by Jean Guegant on + * https://jguegant.github.io/blogs/tech/sfinae-introduction.html + */ +#ifndef DUMUX_TYPETRAITS_ISVALID_HH +#define DUMUX_TYPETRAITS_ISVALID_HH + +#include <type_traits> + +namespace Dumux { + +namespace Impl { + +// the functor testing an expression for validity +// Expression: can be for example a lambda expression +template <typename Expression> +struct ValidityTestFunctor +{ +private: + // std::declval creates an object of expression + // the expression, i.e. a lambda expression gets an object as a parameter and does checks on it. + // so we create also an object of the parameter usiung std::declval + // if decltype can evaluate the type, i.e. the object parameter is a valid argument for the expression + // we return std::true_type + // note: the int is used to give the first overload always precedence + // note: the last argument in decltype determines the deduced type but all types need to be valid + template <typename Argument> + constexpr auto testArgument_(int /* unused int to make this overload the priority choice */) const + -> decltype(std::declval<Expression>()(std::declval<Argument>()), std::true_type()) + { return std::true_type(); } + + // otherwise we return std::false_type, i.e. this is the fallback + template <typename Argument> + constexpr std::false_type testArgument_(...) const + { return std::false_type(); } + +public: + // the operator () takes the argument we want to use as argument to the test expression + // note we use the int to prefer the "valid"-result overload of test_ if possible + template <typename Argument> + constexpr auto operator() (const Argument& arg) const + { return testArgument_<Argument>(int()); } + + // check function takes the template argument explicitly + template <typename Argument> + constexpr auto check () const + { return testArgument_<Argument>(int()); } +}; + +} // end namespace Impl + + +/*! + * \ingroup Common + * \ingroup TypeTraits + * \brief A function that creates a test functor to do class member introspection at compile time + * \return a functor that returns true if the expression is valid with a given type / object + * Usage: + * If you want to test if a class has the member function resize(std::size_t) create a test functor + * \code + * constexpr auto hasResize = isValid([](auto&& c) -> decltype(c.resize(std::size_t(1))) {}; }); + * \endcode + * The you can use the test in compile time expressions + * \code + * template<class T> + * auto otherFunc(const T& t) + * -> typename std::enable_if_t<!decltype(hasResize(myvector))::value, double> + * { return 4.0; } + * \endcode + */ +template <typename Expression> +constexpr auto isValid(const Expression& t) +{ return Impl::ValidityTestFunctor<Expression>(); } + +} // end namespace Dumux + +#endif diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index ea0bdb19633e37b326a1675139f36d13c0f6afd4..6d8f4f2ea9f7328a92665453b2fe85f4155fcdf9 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(math) add_subdirectory(parameters) add_subdirectory(propertysystem) add_subdirectory(spline) +add_subdirectory(typetraits) diff --git a/test/common/typetraits/CMakeLists.txt b/test/common/typetraits/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..48df1479fd3bd5e3a7de18601a5abd58a9b37b06 --- /dev/null +++ b/test/common/typetraits/CMakeLists.txt @@ -0,0 +1 @@ +dune_add_test(SOURCES test_isvalid.cc) diff --git a/test/common/typetraits/test_isvalid.cc b/test/common/typetraits/test_isvalid.cc new file mode 100644 index 0000000000000000000000000000000000000000..996bfaa92246532566ea80479958b918d3636345 --- /dev/null +++ b/test/common/typetraits/test_isvalid.cc @@ -0,0 +1,73 @@ +#include <config.h> + +#include <iostream> +#include <dumux/common/typetraits/isvalid.hh> + +namespace Dumux { +namespace Test { + +struct MyVector { +public: + void resize(std::size_t i) {}; + + void resize(std::size_t i) const {}; + + void resize() {}; +}; + +struct MyOtherVector { + double resize; +}; + +constexpr auto hasResize = Dumux::isValid([](auto&& a) -> decltype(a.resize(std::size_t(1))) { }); + +// using the check function +template<class Vector, typename std::enable_if_t<hasResize.template check<Vector>(), int> = 0> +void resize(const Vector& v, std::size_t size) +{ + v.resize(size); std::cout << "-> resized resizeable vector! size: " << size << std::endl; +} + +// using decltype + declval and operator () +template<class Vector, typename std::enable_if_t<!decltype(hasResize(std::declval<Vector>()))::value, int> = 0> +void resize(const Vector& v, std::size_t size) +{ + std::cout << "-> Did not resize non-resizeable vector!" << std::endl; +} + +// using trailing return type and operator () +template<class Vector> +auto resize2(const Vector& v, std::size_t size) +-> typename std::enable_if_t<decltype(hasResize(v))::value, void> +{ + v.resize(size); std::cout << "-> resized resizeable vector! size: " << size << std::endl; +} + +// using trailing return type and operator () +template<class Vector> +auto resize2(const Vector& v, std::size_t size) +-> typename std::enable_if_t<!hasResize.template check<Vector>(), void> +{ + std::cout << "-> Did not resize non-resizeable vector!" << std::endl; +} + +} // end namespace Test +} // end namespace Dumux + +int main(int argc, char* argv[]) +{ + using namespace Dumux::Test; + + MyVector v0; + MyOtherVector v1; + + static_assert(hasResize(v0), "Vector v0 doesn't have a resize function!"); + static_assert(!hasResize(v1), "False positive. v1 has no resize function!"); + + resize(v0, 3); + resize(v1, 3); + resize2(v0, 10); + resize2(v1, 10); + + return 0; +}