Skip to content

Commit

Permalink
Full coverage for is_functor_argument_immutable test
Browse files Browse the repository at this point in the history
* Test the Traits::is_functor_argument_immutable with all possible cv
and ref qualifiers and actual lambda objects. It appears this got broken
at some point.
* Improve the implementation of is_functor_argument_immutable to be
clearer on how its magic works. Hopefully the EDG frontend and MSVC also
understand this implementation.

Signed-off-by: Matthias Kretz <kretz@kde.org>
  • Loading branch information
mattkretz committed Feb 16, 2016
1 parent e26e671 commit 1f28a42
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 30 deletions.
90 changes: 74 additions & 16 deletions tests/type_traits.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This file is part of the Vc library. {{{
Copyright © 2013-2015 Matthias Kretz <kretz@kde.org>
Copyright © 2013-2016 Matthias Kretz <kretz@kde.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -115,34 +115,92 @@ TEST(hasContiguousStorage)
hasContiguousStorageImpl(h.begin(), "std::array<int, 3>::iterator");
}

struct F0
{
struct F0 {
template <typename T> void operator()(T &) const {}
};
struct F1
{
struct F1 {
template <typename T> void operator()(const T &) const {}
};
struct F2
{
struct F2 {
template <typename T> void operator()(T) const {}
};
struct F3
{
struct F3 {
template <typename T> void operator()(const T) const {}
};
struct F4
{
struct F4 {
// this could be a reference argument but with move semantics. Then, the caller cannot
// see changes to the argument as the variable is in an undefined state after the
// call.
// But as forwarding reference T can also bind as T &, in which case the argument is
// mutable.
template <typename T> void operator()(T &&) const {}
};
void fun1(int &) {}
void fun2(const int &) {}

TEST(test_is_functor_argument_immutable)
{
VERIFY(!(Vc::Traits::is_functor_argument_immutable<F0, int>::value));
VERIFY( (Vc::Traits::is_functor_argument_immutable<F1, int>::value));
VERIFY( (Vc::Traits::is_functor_argument_immutable<F2, int>::value));
VERIFY( (Vc::Traits::is_functor_argument_immutable<F3, int>::value));
VERIFY(!(Vc::Traits::is_functor_argument_immutable<F4, int>::value));
using Vc::Traits::is_functor_argument_immutable;
VERIFY(!(is_functor_argument_immutable<F0, int>::value));
VERIFY( (is_functor_argument_immutable<F1, int>::value));
VERIFY( (is_functor_argument_immutable<F2, int>::value));
VERIFY( (is_functor_argument_immutable<F3, int>::value));
VERIFY(!(is_functor_argument_immutable<F4, int>::value));

VERIFY(!(is_functor_argument_immutable<decltype(fun1), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(fun2), int>::value));

int x = 0;
auto && int_lambda = [&](int) { x += 1; };
auto && int_lambda_l = [&](int &) { x += 1; };
auto && int_lambda_r = [&](int &&) { x += 1; };
auto &&c_int_lambda = [&](const int) { x += 1; };
auto &&c_int_lambda_l = [&](const int &) { x += 1; };
auto &&c_int_lambda_r = [&](const int &&) { x += 1; };
auto &&v_int_lambda = [&](volatile int) { x += 1; };
auto &&v_int_lambda_l = [&](volatile int &) { x += 1; };
auto &&v_int_lambda_r = [&](volatile int &&) { x += 1; };
auto &&cv_int_lambda = [&](const volatile int) { x += 1; };
auto &&cv_int_lambda_l = [&](const volatile int &) { x += 1; };
auto &&cv_int_lambda_r = [&](const volatile int &&) { x += 1; };
VERIFY( (is_functor_argument_immutable<decltype( int_lambda ), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype( int_lambda_l), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype( int_lambda_r), int>::value));
VERIFY( (is_functor_argument_immutable<decltype( c_int_lambda ), int>::value));
VERIFY( (is_functor_argument_immutable<decltype( c_int_lambda_l), int>::value));
VERIFY( (is_functor_argument_immutable<decltype( c_int_lambda_r), int>::value));
VERIFY( (is_functor_argument_immutable<decltype( v_int_lambda ), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype( v_int_lambda_l), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype( v_int_lambda_r), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(cv_int_lambda ), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(cv_int_lambda_l), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(cv_int_lambda_r), int>::value));
#ifdef Vc_CXX14
auto &&auto_lambda = [&](auto) { x += 1; };
auto &&auto_lambda_l = [&](auto &) { x += 1; };
auto &&auto_lambda_r = [&](auto &&) { x += 1; };
auto &&c_auto_lambda = [&](const auto) { x += 1; };
auto &&c_auto_lambda_l = [&](const auto &) { x += 1; };
auto &&c_auto_lambda_r = [&](const auto &&) { x += 1; };
auto &&v_auto_lambda = [&](volatile auto) { x += 1; };
auto &&v_auto_lambda_l = [&](volatile auto &) { x += 1; };
auto &&v_auto_lambda_r = [&](volatile auto &&) { x += 1; };
auto &&cv_auto_lambda = [&](const volatile auto) { x += 1; };
auto &&cv_auto_lambda_l = [&](const volatile auto &) { x += 1; };
auto &&cv_auto_lambda_r = [&](const volatile auto &&) { x += 1; };
VERIFY( (is_functor_argument_immutable<decltype(auto_lambda ), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype(auto_lambda_l), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype(auto_lambda_r), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(c_auto_lambda ), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(c_auto_lambda_l), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(c_auto_lambda_r), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(v_auto_lambda ), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype(v_auto_lambda_l), int>::value));
VERIFY(!(is_functor_argument_immutable<decltype(v_auto_lambda_r), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(cv_auto_lambda ), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(cv_auto_lambda_l), int>::value));
VERIFY( (is_functor_argument_immutable<decltype(cv_auto_lambda_r), int>::value));
#endif
}

TEST(test_is_output_iterator)
Expand Down
59 changes: 45 additions & 14 deletions traits/is_functor_argument_immutable.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This file is part of the Vc library. {{{
Copyright © 2014-2015 Matthias Kretz <kretz@kde.org>
Copyright © 2014-2016 Matthias Kretz <kretz@kde.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -35,23 +35,54 @@ namespace Traits
{
namespace is_functor_argument_immutable_impl
{
// this indirection for decltype is required for EDG based compilers
template <typename F, typename A> struct workaround_edg
{
typedef decltype(&F::template operator()<A>) type;
};
template <typename F, typename A> std::false_type test(void (F::*)(A &));
template <typename F, typename A> std::false_type test(void (F::*)(A &) const);
template <typename F, typename A> std:: true_type test(void (F::*)(const A &));
template <typename F, typename A> std:: true_type test(void (F::*)(const A &) const);
template <typename F, typename A> std:: true_type test(void (F::*)(const A &&));
template <typename F, typename A> std:: true_type test(void (F::*)(const A &&) const);
template <typename F, typename A> std:: true_type test(void (F::*)(const A));
template <typename F, typename A> std:: true_type test(void (F::*)(const A) const);
template <typename F, typename A> std:: true_type test(void (F::*)(A));
template <typename F, typename A> std:: true_type test(void (F::*)(A) const);

// This function is defined with a forwarding reference. Therefore it can also bind as T
// &, in which case the argument is mutable.
template <typename F, typename A> std::false_type test(void (F::*)(A &&));
template <typename F, typename A> std::false_type test(void (F::*)(A &&) const);

// generate a true_type for template operator() members in F that are callable with a
// 'const A &' argument even if the template parameter to operator() is fixed to 'A'.
template <typename F, typename A>
decltype(is_functor_argument_immutable_impl::test(
std::declval<decltype(&F::template operator() < A > )>()))
test2(int);
// generate a true_type for non-template operator() members in F that are callable with a
// 'const A &' argument.
template <typename F, typename A>
decltype(
is_functor_argument_immutable_impl::test(std::declval<decltype(&F::operator())>()))
test2(float);

template <typename A> std::false_type test3(void (*)(A &));
template <typename A> std:: true_type test3(void (*)(const A &));
template <typename A> std:: true_type test3(void (*)(const A));
template <typename A> std:: true_type test3(void (*)(A));
template <typename A> std:: true_type test3(void (*)(A &&));

template <typename F, typename A,
typename MemberPtr = typename workaround_edg<F, A>::type,
typename = decltype((std::declval<F &>().*
(std::declval<MemberPtr>()))(std::declval<const A &>()))>
std::true_type test(int);
template <typename F, typename A> std::false_type test(...);
} // namespace is_functor_argument_immutable_impl

template <typename F, typename A, bool = std::is_function<F>::value>
struct is_functor_argument_immutable;
template <typename F, typename A>
using is_functor_argument_immutable =
decltype(is_functor_argument_immutable_impl::test<F, A>(1));
struct is_functor_argument_immutable<F, A, false>
: public decltype(is_functor_argument_immutable_impl::test2<
typename std::remove_reference<F>::type, A>(int())) {
};
template <typename F, typename A>
struct is_functor_argument_immutable<F, A, true>
: public decltype(is_functor_argument_immutable_impl::test3(std::declval<F>())) {
};

} // namespace Traits
} // namespace Vc
Expand Down

0 comments on commit 1f28a42

Please sign in to comment.