Skip to content

Commit

Permalink
inplace_function: Switch conversion operators to converting construct…
Browse files Browse the repository at this point in the history
…ors.

And two tests, which fail before this patch and succeed after it.

Fixes #125.
  • Loading branch information
Quuxplusone committed Apr 8, 2019
1 parent 3b81451 commit 4004927
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 26 deletions.
53 changes: 27 additions & 26 deletions SG14/inplace_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ template<
>
class inplace_function; // unspecified

namespace inplace_function_detail {
template<class> struct is_inplace_function : std::false_type {};
template<class Sig, size_t Cap, size_t Align>
struct is_inplace_function<inplace_function<Sig, Cap, Align>> : std::true_type {};
} // namespace inplace_function_detail

template<
typename R,
typename... Args,
Expand All @@ -184,10 +190,7 @@ class inplace_function<R(Args...), Capacity, Alignment>
template<
typename T,
typename C = std::decay_t<T>,
typename = std::enable_if_t<
!(std::is_same<C, inplace_function>::value
|| std::is_convertible<C, inplace_function>::value)
>
typename = std::enable_if_t<!inplace_function_detail::is_inplace_function<C>::value>
>
inplace_function(T&& closure)
{
Expand All @@ -214,6 +217,26 @@ class inplace_function<R(Args...), Capacity, Alignment>
::new (std::addressof(storage_)) C{std::forward<T>(closure)};
}

template<size_t Cap, size_t Align>
inplace_function(const inplace_function<R(Args...), Cap, Align>& rhs)
: inplace_function(rhs.vtable_ptr_, rhs.vtable_ptr_->copy_ptr, std::addressof(rhs.storage_))
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Capacity, Alignment, Cap, Align
>::value, "conversion not allowed");
}

template<size_t Cap, size_t Align>
inplace_function(inplace_function<R(Args...), Cap, Align>&& rhs)
: inplace_function(rhs.vtable_ptr_, rhs.vtable_ptr_->relocate_ptr, std::addressof(rhs.storage_))
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Capacity, Alignment, Cap, Align
>::value, "conversion not allowed");

rhs.vtable_ptr_ = std::addressof(inplace_function_detail::empty_vtable<R, Args...>);
}

inplace_function(std::nullptr_t) noexcept :
vtable_ptr_{std::addressof(inplace_function_detail::empty_vtable<R, Args...>)}
{}
Expand Down Expand Up @@ -301,28 +324,6 @@ class inplace_function<R(Args...), Capacity, Alignment>
return vtable_ptr_ != std::addressof(inplace_function_detail::empty_vtable<R, Args...>);
}

template<size_t Cap, size_t Align>
operator inplace_function<R(Args...), Cap, Align>() const&
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Cap, Align, Capacity, Alignment
>::value, "conversion not allowed");

return {vtable_ptr_, vtable_ptr_->copy_ptr, std::addressof(storage_)};
}

template<size_t Cap, size_t Align>
operator inplace_function<R(Args...), Cap, Align>() && noexcept
{
static_assert(inplace_function_detail::is_valid_inplace_dst<
Cap, Align, Capacity, Alignment
>::value, "conversion not allowed");

auto vtable_ptr = std::exchange(vtable_ptr_, std::addressof(inplace_function_detail::empty_vtable<R, Args...>));

return {vtable_ptr, vtable_ptr->relocate_ptr, std::addressof(storage_)};
}

void swap(inplace_function& other) noexcept
{
if (this == std::addressof(other)) return;
Expand Down
53 changes: 53 additions & 0 deletions SG14_test/inplace_function_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,57 @@ static void RvalueRefParameter()
g(std::make_unique<int>(42));
}

static void test_is_convertible()
{
static_assert(std::is_convertible<int(&)(), stdext::inplace_function<int()>>::value, "");
static_assert(std::is_convertible<int(*)(), stdext::inplace_function<int()>>::value, "");
static_assert(std::is_convertible<int(*&)(), stdext::inplace_function<int()>>::value, "");
static_assert(std::is_convertible<int(*&&)(), stdext::inplace_function<int()>>::value, "");
}

namespace {
struct InstrumentedCopyConstructor {
static int copies;
static int moves;
InstrumentedCopyConstructor() = default;
InstrumentedCopyConstructor(const InstrumentedCopyConstructor&) {
copies += 1;
}
InstrumentedCopyConstructor(InstrumentedCopyConstructor&&) {
moves += 1;
}
};
int InstrumentedCopyConstructor::copies = 0;
int InstrumentedCopyConstructor::moves = 0;
} // anonymous namespace

static void test_return_by_move()
{
using IPF20 = stdext::inplace_function<void(), 20>;
using IPF40 = stdext::inplace_function<void(), 40>;
static_assert(std::is_convertible<IPF20, IPF40>::value, "");
static_assert(std::is_convertible<IPF20&, IPF40>::value, "");
static_assert(std::is_convertible<IPF20&&, IPF40>::value, "");
static_assert(std::is_convertible<const IPF20&, IPF40>::value, "");
static_assert(std::is_convertible<const IPF20&&, IPF40>::value, "");

auto foo = []() -> IPF40 {
InstrumentedCopyConstructor cc;
InstrumentedCopyConstructor::copies = 0;
InstrumentedCopyConstructor::moves = 0;
IPF20 f = [cc]() { };
assert(InstrumentedCopyConstructor::copies == 1);
assert(InstrumentedCopyConstructor::moves == 1);
InstrumentedCopyConstructor::copies = 0;
InstrumentedCopyConstructor::moves = 0;
return f;
};
IPF40 f = foo();
assert(InstrumentedCopyConstructor::copies == 0);
assert(InstrumentedCopyConstructor::moves == 1);
}


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

#ifdef TEST_MAIN
Expand Down

0 comments on commit 4004927

Please sign in to comment.