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 Apr 28, 2019
1 parent cdb73d6 commit 62ad577
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 19 deletions.
43 changes: 37 additions & 6 deletions SG14/inplace_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,39 @@ struct is_valid_inplace_dst : std::true_type
);
};

// C++11 MSVC compatible implementation of std::is_invocable_r.

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<
decltype(std::declval<F>()(std::declval<Args>()...), void()),
void,
F,
Args...
> : std::true_type {};

template<class F, class... Args> struct is_invocable_r_impl<
decltype(std::declval<F>()(std::declval<Args>()...), void()),
const void,
F,
Args...
> : std::true_type {};

template<class R, class F, class... Args> struct is_invocable_r_impl<
decltype(accept<R>(std::declval<F>()(std::declval<Args>()...))),
R,
F,
Args...
> : std::true_type {};

template<class R, class F, class... Args> using is_invocable_r = is_invocable_r_impl<
void,
R,
F,
Args...
>;
} // namespace inplace_function_detail

template<
Expand Down Expand Up @@ -190,15 +223,13 @@ class inplace_function<R(Args...), Capacity, Alignment>
template<
typename T,
typename C = std::decay_t<T>,
typename = std::enable_if_t<!inplace_function_detail::is_inplace_function<C>::value>
typename = std::enable_if_t<
!inplace_function_detail::is_inplace_function<C>::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
130 changes: 117 additions & 13 deletions SG14_test/inplace_function_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct ConstFunctor {
void operator()(int i) const { assert(i == expected); called_with = i; }
};

void Foo(int i)
static void Foo(int i)
{
assert(i == expected);
called_with = i;
Expand All @@ -41,14 +41,14 @@ std::string gLastS;
int gLastI = 0;
double gNextReturn = 0.0;

double GlobalFunction(const std::string& s, int i)
static double GlobalFunction(const std::string& s, int i)
{
gLastS = s;
gLastI = i;
return gNextReturn;
}

void FunctionPointer()
static void FunctionPointer()
{
// Even compatible function pointers require an appropriate amount of "storage".
using CompatibleFunctionType = std::remove_reference_t<decltype(GlobalFunction)>;
Expand All @@ -65,7 +65,7 @@ void FunctionPointer()
EXPECT_EQ(42, gLastI);
}

void Lambda()
static void Lambda()
{
stdext::inplace_function<double(int), 8> fun;
std::string closure("some closure");
Expand All @@ -80,7 +80,7 @@ void Lambda()
EXPECT_EQ(42, gLastI);
}

void Bind()
static void Bind()
{
stdext::inplace_function<double(int), 64> fun;
std::string closure("some closure");
Expand Down Expand Up @@ -111,7 +111,7 @@ struct AnotherFunctor
int AnotherFunctor::mDestructorCalls = 0;
int AnotherFunctor::mConstructorCalls = 0;

void FunctorDestruction()
static void FunctorDestruction()
{
AnotherFunctor::mDestructorCalls = 0;
AnotherFunctor::mConstructorCalls = 0;
Expand Down Expand Up @@ -140,7 +140,7 @@ void FunctorDestruction()
EXPECT_EQ(AnotherFunctor::mDestructorCalls, AnotherFunctor::mConstructorCalls);
}

void Swapping()
static void Swapping()
{
AnotherFunctor::mDestructorCalls = 0;
AnotherFunctor::mConstructorCalls = 0;
Expand All @@ -166,7 +166,7 @@ void Swapping()
EXPECT_EQ(AnotherFunctor::mDestructorCalls, AnotherFunctor::mConstructorCalls);
}

void Copying()
static void Copying()
{
auto sptr = std::make_shared<int>(42);
EXPECT_EQ(1, sptr.use_count());
Expand All @@ -190,7 +190,7 @@ void Copying()
EXPECT_TRUE(bool(fun2));
}

void ContainingStdFunction()
static void ContainingStdFunction()
{
// build a big closure, bigger than 32 bytes
uint64_t offset1 = 1234;
Expand All @@ -210,7 +210,7 @@ void ContainingStdFunction()
EXPECT_EQ(r, int(offset1+offset2+offset3+str1.length()+3));
}

void SimilarTypeCopy()
static void SimilarTypeCopy()
{
auto sptr = std::make_shared<int>(42);
EXPECT_EQ(1, sptr.use_count());
Expand All @@ -236,7 +236,7 @@ void SimilarTypeCopy()
fun4 = fun1; // fun1 is bigger than 17, but we should be smart about it
}

void AssignmentDifferentFunctor()
static void AssignmentDifferentFunctor()
{
int calls = 0;
stdext::inplace_function<int(int,int), 16> add = [&calls] (int a, int b) { ++calls; return a+b; };
Expand Down Expand Up @@ -395,7 +395,7 @@ static void test_overloaded_operator_new()
EXPECT_EQ(43, fun(1));
}

void test_move_construction_is_noexcept()
static void test_move_construction_is_noexcept()
{
using IPF = stdext::inplace_function<void(int), sizeof(Functor)>;
std::vector<IPF> vec;
Expand All @@ -407,7 +407,7 @@ void test_move_construction_is_noexcept()
EXPECT_EQ(1, moved);
}

void test_move_construction_from_smaller_buffer_is_noexcept()
static void test_move_construction_from_smaller_buffer_is_noexcept()
{
using IPF32 = stdext::inplace_function<void(int), 32>;
using IPF40 = stdext::inplace_function<void(int), 40>;
Expand Down Expand Up @@ -489,6 +489,108 @@ static void test_return_by_move()
assert(InstrumentedCopyConstructor::moves == 1);
}

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

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

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

static void test_is_invocable()
{
using C_Int1 = int();
using C_Int2 = int(int);
using C_Void = void(int&);

using stdext::inplace_function_detail::is_invocable_r;

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

static_assert(is_invocable_r<int, C_Int2, int>::value, "");
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<void, C_Void, int&>::value, "");
static_assert(! is_invocable_r<void, C_Int1, int&>::value, "");

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

// Testing the conversion from void to int, which should definitely not be allowed.
static_assert(! is_invocable_r<int, C_Void, int&>::value, "");

// cppreference:
// > 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.
static_assert(is_invocable_r<void, C_Int2, int&>::value, "");
static_assert(is_invocable_r<const void, C_Int2, int&>::value, "");

// Regression tests for both is_invocable and is_convertible.
static_assert(is_invocable_r<const int&, int()>::value, "");
static_assert(is_invocable_r<const int&, int(*)()>::value, "");
}

static void test_overloading()
{
EXPECT_EQ(overloaded_function([]() -> int { return 3; }), 3);
EXPECT_EQ(overloaded_function([](int arg) -> int { return arg; }), 42);

using std::is_convertible;
using stdext::inplace_function;

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

const auto b = [](int&) -> void {};
static_assert(is_convertible<decltype(b), inplace_function<void(int&)>>::value, "");
static_assert(!is_convertible<decltype(b), inplace_function<int()>>::value, "");
static_assert(!is_convertible<decltype(b), inplace_function<int(int)>>::value, "");

const auto c = [](int, NoDefaultCtor) -> int { return 3; };
static_assert(is_convertible<decltype(c), inplace_function<void(int, NoDefaultCtor)>>::value, "");
static_assert(!is_convertible<decltype(c), inplace_function<int()>>::value, "");
static_assert(!is_convertible<decltype(c), inplace_function<int(int)>>::value, "");

const auto d = []() -> void {};
static_assert(is_convertible<decltype(d), inplace_function<void()>>::value, "");
static_assert(!is_convertible<decltype(d), inplace_function<int()>>::value, "");
static_assert(!is_convertible<decltype(d), inplace_function<int(int)>>::value, "");

static_assert(is_convertible<int(), inplace_function<const int&()>>::value, "");
static_assert(is_convertible<int(*)(), inplace_function<const int&()>>::value, "");

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

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

void sg14_test::inplace_function_test()
{
Expand Down Expand Up @@ -576,6 +678,8 @@ void sg14_test::inplace_function_test()
test_move_construction_from_smaller_buffer_is_noexcept();
test_is_convertible();
test_return_by_move();
test_is_invocable();
test_overloading();
}

#ifdef TEST_MAIN
Expand Down

0 comments on commit 62ad577

Please sign in to comment.