Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should int(*)() be convertible to inplace_function<void()>? #159

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions SG14/inplace_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ template<typename R, typename... Args> struct vtable

template<typename C> explicit constexpr vtable(wrapper<C>) noexcept :
invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args) -> R
{ return (*static_cast<C*>(storage_ptr))(
static_cast<Args&&>(args)...
); }
{
return static_cast<R>((*static_cast<C*>(storage_ptr))(
static_cast<Args&&>(args)...
));
}
},
copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void
{ ::new (dst_ptr) C{ (*static_cast<C*>(src_ptr)) }; }
Expand Down Expand Up @@ -151,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
56 changes: 55 additions & 1 deletion 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,16 +507,32 @@ 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; };
#if SG14_INPLACE_FUNCTION_INVOCABLE_AS_VOID
static_assert(std::is_convertible<decltype(lambda), stdext::inplace_function<void()>>::value, "");

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

namespace {
struct InstrumentedCopyConstructor {
static int copies;
Expand Down Expand Up @@ -568,11 +591,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 @@ -583,9 +617,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 @@ -616,6 +655,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 @@ -703,11 +753,15 @@ void sg14_test::inplace_function_test()
test_is_convertible();
test_convertibility_with_qualified_call_operators();
test_convertibility_with_lambdas();
test_void_returning_function_runtime();
test_return_by_move();
test_is_invocable();
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