// Copyright ⓒ 2018-2021 ThePhD. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // See https://github.com/ThePhD/out_ptr/blob/master/docs/out_ptr.adoc for documentation. #include #include #include #include namespace ztd { template class special_out_customization_handle : public handle { private: using base_t = handle; public: using base_t::base_t; }; } // namespace ztd namespace ztd { namespace out_ptr { // we have null-returning functions in these tests, // so we can test this theoretical optimized smart pointer, which is identical to the // clever_out_ptr implementation template class out_ptr_t, Pointer, Args...> : std::tuple { private: using Smart = ztd::special_out_customization_handle; using source_pointer = pointer_of_or_t; using ArgsTuple = std::tuple; using Base = ArgsTuple; Smart* m_smart_ptr; source_pointer m_old_ptr; Pointer* m_target_ptr; public: out_ptr_t(Smart& s, Args... args) noexcept : Base(std::forward(args)...), m_smart_ptr(std::addressof(s)), m_old_ptr(s.get()), m_target_ptr(reinterpret_cast(std::addressof(this->m_smart_ptr->get()))) { } out_ptr_t(out_ptr_t&& right) noexcept : Base(std::move(right)), m_smart_ptr(right.m_smart_ptr), m_old_ptr(right.m_old_ptr), m_target_ptr(right.m_target_ptr) { right.m_old_ptr = nullptr; } out_ptr_t& operator=(out_ptr_t&& right) noexcept { Base::operator =(std::move(right)); this->m_smart_ptr = right.m_smart_ptr; this->m_old_ptr = right.m_old_ptr; this->m_target_ptr = right.m_target_ptr; right.m_old_ptr = nullptr; return *this; } operator Pointer*() const noexcept { return const_cast(this->m_target_ptr); } ~out_ptr_t() noexcept { static_assert(sizeof...(Args) < 1, "you cannot reset the deleter for handle!: it only takes one argument!"); if (this->m_old_ptr != nullptr) { this->m_smart_ptr->get_deleter()(this->m_old_ptr); } } }; }} // namespace ztd::out_ptr TEST_CASE("out_ptr/customization basic", "out_ptr type works with smart pointers and C-style output APIs") { SECTION("handle") { ztd::special_out_customization_handle> p(nullptr); ficapi_create(ztd::out_ptr::out_ptr(p), ficapi_type::ficapi_type_int); int* rawp = static_cast(p.get()); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); } SECTION("handle") { ztd::special_out_customization_handle p(nullptr); ficapi_int_create(ztd::out_ptr::out_ptr(p)); int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); } SECTION("handle") { ztd::special_out_customization_handle p(nullptr); ficapi_handle_create(ztd::out_ptr::out_ptr(p)); ficapi::opaque_handle rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(ficapi_handle_get_data(rawp) == ficapi_get_dynamic_data()); } SECTION("handle, void out_ptr") { ztd::special_out_customization_handle p(nullptr); ficapi_create(ztd::out_ptr::out_ptr(p), ficapi_type::ficapi_type_opaque); ficapi::opaque_handle rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(ficapi_handle_get_data(rawp) == ficapi_get_dynamic_data()); } SECTION("handle, ficapi::opaque_handle out_ptr") { ztd::special_out_customization_handle> p(nullptr); ficapi_create(ztd::out_ptr::out_ptr(p), ficapi::type::ficapi_type_opaque); ficapi::opaque_handle rawp = static_cast(p.get()); REQUIRE(rawp != nullptr); REQUIRE(ficapi_handle_get_data(rawp) == ficapi_get_dynamic_data()); } } TEST_CASE("out_ptr/customization stateful", "out_ptr type works with stateful deleters in smart pointers") { SECTION("handle") { ztd::special_out_customization_handle p(nullptr, ficapi::stateful_deleter{ 0x12345678, ficapi_type::ficapi_type_int }); ficapi_create(ztd::out_ptr::out_ptr(p), ficapi_type::ficapi_type_int); int* rawp = static_cast(p.get()); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().state() == 0x12345678); } SECTION("handle") { ztd::special_out_customization_handle p(nullptr, ficapi::stateful_int_deleter{ 0x12345678 }); ficapi_int_create(ztd::out_ptr::out_ptr(p)); int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().state() == 0x12345678); } SECTION("handle") { ztd::special_out_customization_handle p(nullptr, ficapi::stateful_handle_deleter{ 0x12345678 }); ficapi_handle_create(ztd::out_ptr::out_ptr(p)); ficapi::opaque_handle rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(ficapi_handle_get_data(rawp) == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().state() == 0x12345678); } SECTION("handle, void out_ptr") { ztd::special_out_customization_handle p(nullptr, ficapi::stateful_deleter{ 0x12345678, ficapi_type::ficapi_type_int }); ficapi_create(ztd::out_ptr::out_ptr(p), ficapi_type::ficapi_type_opaque); ficapi::opaque_handle rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(ficapi_handle_get_data(rawp) == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().state() == 0x12345678); } } TEST_CASE("out_ptr/customization reused", "out_ptr type properly deletes non-nullptr types from earlier") { struct reused_deleter { int store = 0; void operator()(void* x) { ++store; ficapi_delete(x, ficapi_type::ficapi_type_int); } }; struct reused_int_deleter { int store = 0; void operator()(int* x) { ++store; ficapi_int_delete(x); } }; SECTION("handle") { ztd::special_out_customization_handle p(nullptr, reused_deleter{}); ficapi_create(ztd::out_ptr::out_ptr(p), ficapi_type::ficapi_type_int); { int* rawp = static_cast(p.get()); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 0); } ficapi_create(ztd::out_ptr::out_ptr(p), ficapi_type::ficapi_type_int); { int* rawp = static_cast(p.get()); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 1); } ficapi_int_create(ztd::out_ptr::out_ptr(p)); { int* rawp = static_cast(p.get()); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 2); } } SECTION("handle") { ztd::special_out_customization_handle p(nullptr, reused_int_deleter{}); ficapi_int_create(ztd::out_ptr::out_ptr(p)); { int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 0); } ficapi_int_create(ztd::out_ptr::out_ptr(p)); { int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 1); } ficapi_create(ztd::out_ptr::out_ptr(p), ficapi_type::ficapi_type_int); { int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 2); } } }