Skip to content
Snippets Groups Projects
Commit f55de80a authored by Timo Koch's avatar Timo Koch
Browse files

[typetraits] Implement isValid function to generate class member introspection functors

* isValid can be used to generate a functor that can check whether an operation done with a type is valid
* this patch also provides examples in form of a test and documentation
parent 635a650a
No related branches found
No related tags found
1 merge request!797[typetraits] Implement isValid function to generate class member introspection functors
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
install(FILES install(FILES
matrix.hh matrix.hh
vector.hh vector.hh
isvalid.hh
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/common/typetraits) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dumux/common/typetraits)
// -*- 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
...@@ -4,3 +4,4 @@ add_subdirectory(math) ...@@ -4,3 +4,4 @@ add_subdirectory(math)
add_subdirectory(parameters) add_subdirectory(parameters)
add_subdirectory(propertysystem) add_subdirectory(propertysystem)
add_subdirectory(spline) add_subdirectory(spline)
add_subdirectory(typetraits)
dune_add_test(SOURCES test_isvalid.cc)
#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;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment