Skip to content
Snippets Groups Projects
Commit 381de64f authored by Carsten Gräser's avatar Carsten Gräser
Browse files

Merge branch 'feature/is_callable' into 'master'

Add implementation of std::is_callable

This is proposed for C++17 and helpful to checking correctness
of predicates supplied to algorithms. This also commes with
a test that checks is the behaviour is correct, especially
with respect to various combinations of r-values, r-value references,
and l-value-references.

See merge request !151
parents fe05ec34 48408e3b
No related branches found
No related tags found
No related merge requests found
// -*- 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
......
......@@ -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)
......
// -*- 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();
}
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