Skip to content

Commit

Permalink
inplace_function: is_convertible<int(), inplace_function<void()>>.
Browse files Browse the repository at this point in the history
Put the "standard" behavior under a macro flag, and default to
the "safer" behavior. That is, disallow the conversion by default.
  • Loading branch information
Quuxplusone committed May 23, 2019
1 parent 50c3b31 commit 4d19a47
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 3 deletions.
11 changes: 11 additions & 0 deletions SG14/inplace_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,31 @@ struct is_valid_inplace_dst : std::true_type
};

// C++11 MSVC compatible implementation of std::is_invocable_r.
// By default, our is_invocable_r<void, int()> is FALSE,
// even though std::is_invocable_r<void, int()> is TRUE.
// Set SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID if you want that behavior.

template<class R> void accept(R);

template<class, class R, class F, class... Args> struct is_invocable_r_impl : std::false_type {};

template<class F, class... Args> struct is_invocable_r_impl<
#if SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID
decltype(std::declval<F>()(std::declval<Args>()...), void()),
#else
decltype(std::declval<F>()(std::declval<Args>()...)),
#endif
void,
F,
Args...
> : std::true_type {};

template<class F, class... Args> struct is_invocable_r_impl<
#if SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID
decltype(std::declval<F>()(std::declval<Args>()...), void()),
#else
decltype(std::declval<F>()(std::declval<Args>()...)),
#endif
const void,
F,
Args...
Expand Down
51 changes: 48 additions & 3 deletions SG14_test/inplace_function_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ static void test_convertibility_with_lambdas()

const auto a = []() -> int { return 3; };
static_assert(std::is_convertible<decltype(a), stdext::inplace_function<int()>>::value, "");
static_assert(std::is_convertible<decltype(a), stdext::inplace_function<long()>>::value, "");
static_assert(!std::is_convertible<decltype(a), stdext::inplace_function<int(int)>>::value, "");
static_assert(!std::is_convertible<decltype(a), stdext::inplace_function<void(int&)>>::value, "");

Expand All @@ -485,7 +486,13 @@ static void test_convertibility_with_lambdas()
static_assert(!std::is_convertible<decltype(b), stdext::inplace_function<int(int)>>::value, "");

const auto c = [](int, NoDefaultCtor) -> int { return 3; };
static_assert(std::is_convertible<decltype(c), stdext::inplace_function<int(int, NoDefaultCtor)>>::value, "");
static_assert(std::is_convertible<decltype(c), stdext::inplace_function<long(int, NoDefaultCtor)>>::value, "");
#if SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID
static_assert(std::is_convertible<decltype(c), stdext::inplace_function<void(int, NoDefaultCtor)>>::value, "");
#else
static_assert(!std::is_convertible<decltype(c), stdext::inplace_function<void(int, NoDefaultCtor)>>::value, "");
#endif
static_assert(!std::is_convertible<decltype(c), stdext::inplace_function<int()>>::value, "");
static_assert(!std::is_convertible<decltype(c), stdext::inplace_function<int(int)>>::value, "");

Expand All @@ -500,29 +507,37 @@ static void test_convertibility_with_lambdas()
// Same as a, but not const.
auto e = []() -> int { return 3; };
static_assert(std::is_convertible<decltype(e), stdext::inplace_function<int()>>::value, "");
static_assert(std::is_convertible<decltype(e), stdext::inplace_function<long()>>::value, "");
static_assert(!std::is_convertible<decltype(e), stdext::inplace_function<int(int)>>::value, "");
static_assert(!std::is_convertible<decltype(e), stdext::inplace_function<void(int&)>>::value, "");

// Same as a, but not const and mutable.
auto f = []() mutable -> int { return 3; };
static_assert(std::is_convertible<decltype(f), stdext::inplace_function<int()>>::value, "");
static_assert(std::is_convertible<decltype(f), stdext::inplace_function<long()>>::value, "");
static_assert(!std::is_convertible<decltype(f), stdext::inplace_function<int(int)>>::value, "");
static_assert(!std::is_convertible<decltype(f), stdext::inplace_function<void(int&)>>::value, "");
}

static void test_void_returning_function_runtime()
{
auto lambda = []() { return 42; };
using CVoid = const void;
#if SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID
static_assert(std::is_convertible<decltype(lambda), stdext::inplace_function<void()>>::value, "");
static_assert(std::is_convertible<decltype(lambda), stdext::inplace_function<const void()>>::value, "");
static_assert(std::is_convertible<decltype(lambda), stdext::inplace_function<CVoid()>>::value, "");

stdext::inplace_function<void()> f = lambda;
f = lambda;
f();

stdext::inplace_function<const void()> g = lambda;
stdext::inplace_function<CVoid()> g = lambda;
g = lambda;
g();
#else
static_assert(!std::is_convertible<decltype(lambda), stdext::inplace_function<void()>>::value, "");
static_assert(!std::is_convertible<decltype(lambda), stdext::inplace_function<CVoid()>>::value, "");
#endif
}

namespace {
Expand Down Expand Up @@ -583,11 +598,22 @@ static void test_is_invocable()
static_assert(! is_invocable_r<int, C_Int1, int>::value, "");
static_assert(! is_invocable_r<int, C_Void, int>::value, "");

static_assert(is_invocable_r<const int, C_Int2, int>::value, "");
static_assert(! is_invocable_r<const int, C_Int1, int>::value, "");
static_assert(! is_invocable_r<const int, C_Void, int>::value, "");

static_assert(is_invocable_r<void, C_Void, int&>::value, "");
static_assert(! is_invocable_r<void, C_Int1, int&>::value, "");

static_assert(is_invocable_r<const void, C_Void, int&>::value, "");
static_assert(! is_invocable_r<const void, C_Int1, int&>::value, "");

// Testing widening and narrowing conversions, and the "conversion" to void.
#if SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID
static_assert(is_invocable_r<void, C_Int1>::value, "");
#else
static_assert(! is_invocable_r<void, C_Int1>::value, "");
#endif
static_assert(is_invocable_r<long, C_Int1>::value, "");
static_assert(is_invocable_r<char, C_Int1>::value, "");

Expand All @@ -598,9 +624,14 @@ static void test_is_invocable()
// > Determines whether Fn can be invoked with the arguments ArgTypes...
// > to yield a result that is convertible to R.
//
// void is treated specially because a functions return value can be ignored.
// void is treated specially because a function's return value can be ignored.
#if SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID
static_assert(is_invocable_r<void, C_Int2, int&>::value, "");
static_assert(is_invocable_r<const void, C_Int2, int&>::value, "");
#else
static_assert(! is_invocable_r<void, C_Int2, int&>::value, "");
static_assert(! is_invocable_r<const void, C_Int2, int&>::value, "");
#endif

// Regression tests for both is_invocable and is_convertible.
static_assert(is_invocable_r<const int&, int()>::value, "");
Expand Down Expand Up @@ -631,6 +662,17 @@ static void test_overloading_on_return_type()
EXPECT_EQ(overloaded_function3([](int) { return nullptr; }), 2);
}

#if !(SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID)
static int overloaded_function4(stdext::inplace_function<void()>) { return 1; }
static int overloaded_function4(stdext::inplace_function<int()>) { return 2; }

static void test_overloading_on_return_type_void()
{
EXPECT_EQ(overloaded_function4([]() -> void {}), 1);
EXPECT_EQ(overloaded_function4([]() -> int { return 0; }), 2);
}
#endif

void sg14_test::inplace_function_test()
{
// first set of tests (from Optiver)
Expand Down Expand Up @@ -724,6 +766,9 @@ void sg14_test::inplace_function_test()
test_overloading_on_arity();
test_overloading_on_parameter_type();
test_overloading_on_return_type();
#if !(SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID)
test_overloading_on_return_type_void();
#endif
}

#ifdef TEST_MAIN
Expand Down

0 comments on commit 4d19a47

Please sign in to comment.