diff --git a/dune/common/std/type_traits.hh b/dune/common/std/type_traits.hh index b5c09a46a82dffe25b2fbfec6df46a028275318d..2862e028343fb04d807f033b595e82309c2b5d5c 100644 --- a/dune/common/std/type_traits.hh +++ b/dune/common/std/type_traits.hh @@ -1,7 +1,11 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: #ifndef DUNE_COMMON_STD_TYPE_TRAITS_HH #define DUNE_COMMON_STD_TYPE_TRAITS_HH #include <type_traits> +#include <dune/common/typetraits.hh> +#include <dune/common/typeutilities.hh> namespace Dune { @@ -95,6 +99,82 @@ namespace Std #endif + + namespace Imp { + + // If R is void we only need to check if F can be called + // with given Args... list. If this is not possible + // result_of_t is not defined and this overload is disabled. + template<class R, class F, class... Args, + std::enable_if_t< + std::is_same<void_t<std::result_of_t<F(Args...)>>, R>::value + , int> = 0> + std::true_type is_callable_helper(PriorityTag<2>) + { return {}; } + + // Check if result of F(Args...) can be converted to R. + // If F cannot even be called with given Args... then + // result_of_t is not defined and this overload is disabled. + template<class R, class F, class... Args, + std::enable_if_t< + std::is_convertible<std::result_of_t<F(Args...)>, R>::value + , int> = 0> + std::true_type is_callable_helper(PriorityTag<1>) + { return {}; } + + // If none of the above matches, F can either not be called + // with given Args..., or the result cannot be converted to + // void, or R is not void. + template<class R, class F, class... Args> + std::false_type is_callable_helper(PriorityTag<0>) + { return {}; } + } + + /** + * \brief Traits class to check if function is callable + * + * \tparam D Function descriptor + * \tparam R Return value + * + * If D = F(Args...) this checks if F can be called with an + * argument list of type Args..., and if the return value can + * be converted to R. If R is void, any return type is accepted. + * The result is encoded by deriving from std::integral_constant<bool, result>. + * + * If D is not of the form D = F(Args...) this class is not defined. + * + * This implements std::is_callable as proposed in N4446 for C++17. + */ + template <class D, class R= void> + struct is_callable; + + /** + * \brief Traits class to check if function is callable + * + * \tparam D Function descriptor + * \tparam R Return value + * + * If D = F(Args...) this checks if F can be called with an + * argument list of type Args..., and if the return value can + * be converted to R. If R is void, any return type is accepted. + * The result is encoded by deriving from std::integral_constant<bool, result>. + * + * If D is not of the form D = F(Args...) this class is not defined. + * + * This implements std::is_callable as proposed in N4446 for C++17. + */ + template <class F, class... Args, class R> + struct is_callable< F(Args...), R> : + decltype(Imp::is_callable_helper<R, F, Args...>(PriorityTag<42>())) + {}; + + + + + + + + } // namespace Std } // namespace Dune diff --git a/dune/common/test/CMakeLists.txt b/dune/common/test/CMakeLists.txt index 2ca8e6f5e109b9f86cf6a5b3f377c4e3b33c34cd..76907985923cc8817d32c77674333acf05caa022 100644 --- a/dune/common/test/CMakeLists.txt +++ b/dune/common/test/CMakeLists.txt @@ -151,6 +151,9 @@ dune_add_test(SOURCES sllisttest.cc) dune_add_test(SOURCES stdapplytest.cc LINK_LIBRARIES dunecommon) +dune_add_test(SOURCES stdtypetraitstest.cc + LINK_LIBRARIES dunecommon) + dune_add_test(SOURCES streamtest.cc LINK_LIBRARIES dunecommon) diff --git a/dune/common/test/stdtypetraitstest.cc b/dune/common/test/stdtypetraitstest.cc new file mode 100644 index 0000000000000000000000000000000000000000..82a587f3c9c6941b361a519d599d47fbfb887f71 --- /dev/null +++ b/dune/common/test/stdtypetraitstest.cc @@ -0,0 +1,75 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include <dune/common/std/type_traits.hh> + +#include <dune/common/test/testsuite.hh> + + +int main() +{ + Dune::TestSuite test; + + { + auto f = [](int i) { return 0; }; + using F = decltype(f); + + test.check(Dune::Std::is_callable<F(int)>() == true) + << "Dune::Std::is_callable does not accept copy from r-value"; + test.check(Dune::Std::is_callable<F(int&)>() == true) + << "Dune::Std::is_callable does not accept copy from l-value reference"; + test.check(Dune::Std::is_callable<F(int&&)>() == true) + << "Dune::Std::is_callable does not accept copy from r-value reference"; + + test.check(Dune::Std::is_callable<F(std::string)>() == false) + << "Dune::Std::is_callable accepts invalid argument type"; + test.check(Dune::Std::is_callable<F(int, int)>() == false) + << "Dune::Std::is_callable accepts invalid argument count"; + test.check(Dune::Std::is_callable<F(int), int>() == true) + << "Dune::Std::is_callable does not accept valid return type"; + test.check(Dune::Std::is_callable<F(int), std::string>() == false) + << "Dune::Std::is_callable accepts invalid return type"; + } + + { + auto f = [](const int& i) {}; + using F = decltype(f); + + test.check(Dune::Std::is_callable<F(int)>() == true) + << "Dune::Std::is_callable does not accept const& temporary from r-value"; + test.check(Dune::Std::is_callable<F(int&)>() == true) + << "Dune::Std::is_callable does not accept const& temporary from l-value reference"; + test.check(Dune::Std::is_callable<F(int&&)>() == true) + << "Dune::Std::is_callable does not accept const& temporary from r-value reference"; + } + + { + auto f = [](int& i) {}; + using F = decltype(f); + + test.check(Dune::Std::is_callable<F(int)>() == false) + << "Dune::Std::is_callable accepts l-value reference from r-value"; + test.check(Dune::Std::is_callable<F(int&)>() == true) + << "Dune::Std::is_callable does not accept l-value reference from l-value reference"; + test.check(Dune::Std::is_callable<F(int&&)>() == false) + << "Dune::Std::is_callable accepts l-value reference from r-value reference"; + } + + { + auto f = [](int&& i) {}; + using F = decltype(f); + + test.check(Dune::Std::is_callable<F(int)>() == true) + << "Dune::Std::is_callable does not accept r-value reference from r-value"; + test.check(Dune::Std::is_callable<F(int&)>() == false) + << "Dune::Std::is_callable accepts r-value reference from l-value reference"; + test.check(Dune::Std::is_callable<F(int&&)>() == true) + << "Dune::Std::is_callable does not accept r-value reference from r-value reference"; + } + + return test.exit(); +}