// 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 #include // A template that always evaluates to false anyhow template struct always_false_index : std::integral_constant {}; namespace ztd { template class special_inout_customization_handle : public handle { private: using base_t = handle; public: using base_t::base_t; }; } // namespace ztd namespace ztd { namespace out_ptr { template class inout_ptr_t, Pointer, Args...> : std::tuple { private: using Smart = ztd::special_inout_customization_handle; using source_pointer = pointer_of_or_t; using ArgsTuple = std::tuple; using Base = ArgsTuple; Pointer* m_target_ptr; public: inout_ptr_t(Smart& s, Args... args) noexcept : Base(std::forward(args)...), m_target_ptr(reinterpret_cast(std::addressof(s.get()))) { } inout_ptr_t(inout_ptr_t&& right) noexcept : Base(std::move(right)), m_target_ptr(right.m_target_ptr) { } inout_ptr_t& operator=(inout_ptr_t&& right) noexcept { Base::operator =(std::move(right)); this->m_target_ptr = right.m_target_ptr; return *this; } operator Pointer*() const noexcept { return const_cast(this->m_target_ptr); } ~inout_ptr_t() noexcept { static_assert(sizeof...(Args) < 1, "you cannot reset the deleter for handle!: it only takes one argument!"); } }; }} // namespace ztd::out_ptr TEST_CASE("inout_ptr/customization basic", "inout_ptr type works with smart pointers and C-style output APIs") { SECTION("handle") { ztd::special_inout_customization_handle> p(nullptr); ficapi_re_create(ztd::out_ptr::inout_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_inout_customization_handle p(nullptr); ficapi_int_re_create(ztd::out_ptr::inout_ptr(p)); int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); } SECTION("handle") { ztd::special_inout_customization_handle p(nullptr); ficapi_handle_re_create(ztd::out_ptr::inout_ptr(p)); ficapi::opaque_handle rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(ficapi_handle_get_data(rawp) == ficapi_get_dynamic_data()); } SECTION("handle, void inout_ptr") { ztd::special_inout_customization_handle p(nullptr); ficapi_re_create(ztd::out_ptr::inout_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()); } #if 0 // this no longer applies because there is no implicit void* conversion... SECTION("handle, ficapi::opaque_handle inout_ptr") { ztd::special_inout_customization_handle> p(nullptr); ficapi_re_create(ztd::out_ptr::inout_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()); } #endif } TEST_CASE("inout_ptr/customization stateful", "inout_ptr type works with stateful deleters in smart pointers") { SECTION("handle") { ztd::special_inout_customization_handle p(nullptr, ficapi::stateful_deleter{ 0x12345678, ficapi_type::ficapi_type_int }); ficapi_re_create(ztd::out_ptr::inout_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_inout_customization_handle p(nullptr, ficapi::stateful_int_deleter{ 0x12345678 }); ficapi_int_re_create(ztd::out_ptr::inout_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_inout_customization_handle p(nullptr, ficapi::stateful_handle_deleter{ 0x12345678 }); ficapi_handle_re_create(ztd::out_ptr::inout_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 inout_ptr") { ztd::special_inout_customization_handle p(nullptr, ficapi::stateful_deleter{ 0x12345678, ficapi_type::ficapi_type_int }); ficapi_re_create(ztd::out_ptr::inout_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("inout_ptr/customization reused", "inout_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_inout_customization_handle p(nullptr, reused_deleter{}); ficapi_re_create(ztd::out_ptr::inout_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_re_create(ztd::out_ptr::inout_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_int_re_create(ztd::out_ptr::inout_ptr(p)); { int* rawp = static_cast(p.get()); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 0); } } SECTION("handle") { ztd::special_inout_customization_handle p(nullptr, reused_int_deleter{}); ficapi_int_re_create(ztd::out_ptr::inout_ptr(p)); { int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 0); } ficapi_int_re_create(ztd::out_ptr::inout_ptr(p)); { int* rawp = p.get(); REQUIRE(rawp != nullptr); REQUIRE(*rawp == ficapi_get_dynamic_data()); REQUIRE(p.get_deleter().store == 0); } ficapi_re_create(ztd::out_ptr::inout_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 == 0); } } }