Skip to content

Commit

Permalink
Make inplace_function SFINAE aware
Browse files Browse the repository at this point in the history
  • Loading branch information
Voultapher committed Mar 31, 2019
1 parent 94debf7 commit 26fd931
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 5 deletions.
76 changes: 71 additions & 5 deletions SG14/inplace_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,76 @@ struct is_valid_inplace_dst : std::true_type
);
};

// C++11 MSVC compatible implementation of std::is_invocable_r,
// std version doesn't seem to respect return types and requires C++17.
// See https://godbolt.org/z/T0bd3K for defect example.

template<
typename Default,
template<typename...> class Expression,
typename... Ts
>
class detected_or
{
private:
template<
template<typename...> class Alias,
typename... Args,
typename Ret_t = Alias<Args...>
>
static auto ov(int ph) -> Ret_t;

template<
template<typename...> class Alias,
typename... Args
>
static Default ov(...);

public:
using type = decltype(ov<Expression, Ts...>(0));
};

template<
typename R,
typename C,
typename... Args
>
using def_closure_return_type_convertible = std::is_convertible<
typename std::decay<R>::type,
decltype(std::declval<C>()(std::declval<Args>()...))
>;

template<
typename R,
typename C,
typename... Args
>
using is_invocable_r = typename detected_or<
std::false_type,
def_closure_return_type_convertible,
R, C, Args...
>::type;

static inline int return_int() { return 3; }
static inline int return_int_with_parameter(int) { return 5; };
static inline void return_void_with_ref_parameter(int&) {}

using C_Int1 = typename std::decay<decltype(return_int)>::type;
using C_Int2 = typename std::decay<decltype(return_int_with_parameter)>::type;
using C_Void = typename std::decay<decltype(return_void_with_ref_parameter)>::type;

static_assert(is_invocable_r<int, C_Int1>::value, "IV A");
static_assert(! is_invocable_r<int, C_Int2>::value, "IV B");
static_assert(! is_invocable_r<int, C_Void>::value, "IV C");

static_assert(is_invocable_r<int, C_Int2, int>::value, "IV D");
static_assert(! is_invocable_r<int, C_Int1, int>::value, "IV E");
static_assert(! is_invocable_r<int, C_Void, int>::value, "IV F");

static_assert(is_invocable_r<void, C_Void, int&>::value, "IV G");
static_assert(! is_invocable_r<void, C_Int1, int&>::value, "IV H");
static_assert(! is_invocable_r<void, C_Int2, int&>::value, "IV I");

} // namespace inplace_function_detail

template<
Expand Down Expand Up @@ -187,15 +257,11 @@ class inplace_function<R(Args...), Capacity, Alignment>
typename = std::enable_if_t<
!(std::is_same<C, inplace_function>::value
|| std::is_convertible<C, inplace_function>::value)
&& inplace_function_detail::is_invocable_r<R, C, Args...>::value
>
>
inplace_function(T&& closure)
{
#if __cplusplus >= 201703L
static_assert(std::is_invocable_r<R, C, Args...>::value,
"inplace_function cannot be constructed from non-callable type"
);
#endif
static_assert(std::is_copy_constructible<C>::value,
"inplace_function cannot be constructed from non-copyable type"
);
Expand Down
43 changes: 43 additions & 0 deletions SG14_test/inplace_function_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,48 @@ static void RvalueRefParameter()
g(std::make_unique<int>(42));
}

struct NoDefaultCtor
{
int val;
explicit NoDefaultCtor(int v) : val{v} {}
};

int overloaded_function(stdext::inplace_function<int()>&& exec)
{
return exec();
}

int overloaded_function(stdext::inplace_function<int(int)>&& exec)
{
return exec(int{42});
}

int overloaded_function(stdext::inplace_function<int(int, NoDefaultCtor)>&& exec)
{
return exec(int{42}, NoDefaultCtor{5});
}

int overloaded_function(stdext::inplace_function<void(int&)>&& exec)
{
int out_parameter{88};
exec(out_parameter);
return out_parameter;
}

int fobau(NoDefaultCtor) { return 2; }


void test_overloading()
{
using brokenType = decltype(fobau(std::declval<NoDefaultCtor>()));
EXPECT_EQ(brokenType{2}, 2);

EXPECT_EQ(overloaded_function([]() -> int { return 3; }), 3);
EXPECT_EQ(overloaded_function([](int arg) -> int { return arg; }), 42);
EXPECT_EQ(overloaded_function([](int arg, NoDefaultCtor ndc) -> int { return arg + ndc.val; }), 42 + 5);
EXPECT_EQ(overloaded_function([](int& arg) -> void { arg += 3; }), 88 + 3);
}

void sg14_test::inplace_function_test()
{
// first set of tests (from Optiver)
Expand Down Expand Up @@ -512,6 +554,7 @@ void sg14_test::inplace_function_test()
test_nullptr();
test_overloaded_operator_new();
test_move_construction_is_noexcept();
test_overloading();
}

#ifdef TEST_MAIN
Expand Down

0 comments on commit 26fd931

Please sign in to comment.