From 7d55dcf1245453210d667a4507734be0e62ef117 Mon Sep 17 00:00:00 2001 From: Avril Date: Sat, 12 Mar 2022 20:13:44 +0000 Subject: [PATCH] With fully incomplete types, `unsafe_cast` is used instead of `try_cast` in `operator [const] T*()`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for opaque_handle's current commit: Curse − 凶 --- include/opaque.hh | 40 +++++++++++++++++++++++++++++++++------- src/main.cpp | 7 +++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/include/opaque.hh b/include/opaque.hh index 635a02e..77cef48 100644 --- a/include/opaque.hh +++ b/include/opaque.hh @@ -15,6 +15,9 @@ struct opaque_bad_cast : $_oe("bad opaque_handle pointer cast"); struct opaque_handle; struct opaque_handle_impl; +template +constexpr inline bool opaque_is_incomplete = !requires { { sizeof(T) }; }; + struct opaque_handle_impl { friend class opaque_handle; @@ -36,10 +39,14 @@ struct opaque_handle_impl { template - inline constexpr const To* try_cast() const noexcept { return is_type(typeid(To)) ? static_cast(as_raw_ptr()) : nullptr; } + inline constexpr const To* try_cast() const noexcept { return is_type() ? static_cast(as_raw_ptr()) : nullptr; } template - inline constexpr To* try_cast() noexcept { return is_type(typeid(To)) ? static_cast(as_raw_ptr()) : nullptr; } + inline constexpr To* try_cast() noexcept { return is_type() ? static_cast(as_raw_ptr()) : nullptr; } + template + constexpr bool is_type() const noexcept { + if constexpr(requires { { sizeof(T) }; }) return is_type(typeid(T)); else return true; + } constexpr virtual bool is_type(const std::type_info& a) const noexcept { if(const auto* p = get_type_info()) return *p == a; return false; // Types are uncomparable. @@ -87,6 +94,7 @@ struct opaque_handle final { template constexpr T* unsafe_cast() noexcept { return _impl ? _impl->unsafe_cast() : nullptr; } + /// WARNING: try_cast(), where T is an incomplete type, will always succeed, which may lead to undefined behaviour. Make sure to account for this template constexpr const T* try_cast() const noexcept { return _impl ? _impl->try_cast() : nullptr; } template @@ -138,9 +146,15 @@ struct opaque_handle final { inline auto operator*() noexcept { return *(try_cast() ?: throw opaque_bad_cast{}); } */ template - constexpr operator T*() noexcept { return try_cast(); } + constexpr operator T*() noexcept { + if constexpr(opaque_is_incomplete) return unsafe_cast(); + else return try_cast(); + } template - constexpr operator const T*() const noexcept { return try_cast(); } + constexpr operator const T*() const noexcept { + if constexpr(opaque_is_incomplete) return unsafe_cast(); + else return try_cast(); + } private: struct _impl_deref_const { @@ -248,7 +262,11 @@ constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handle { return const_cast(data); } constexpr opaque_handle_impl* try_clone() const override final { return data ? new object_handler(*this) : nullptr; } - inline constexpr const std::type_info* get_type_info() const noexcept override final { return &typeid(T); } + inline constexpr const std::type_info* get_type_info() const noexcept override final { + if constexpr(requires { { sizeof(T) }; }) + return &typeid(T); + else return nullptr; + } T* data; const HandleF& handler; @@ -294,7 +312,11 @@ constexpr inline opaque_handle make_opaque_handle(T* data, HandleF&& handler) no return data ? new object_handler(*this) : nullptr; } else return nullptr; } - inline constexpr const std::type_info* get_type_info() const noexcept override final { return &typeid(T); } + inline constexpr const std::type_info* get_type_info() const noexcept override final { + if constexpr(requires { { sizeof(T) }; }) + return &typeid(T); + else return nullptr; + } T* data; HandleF handler; @@ -334,7 +356,11 @@ constexpr inline opaque_handle make_opaque_handle(T* data) noexcept { return const_cast(data); } constexpr opaque_handle_impl* try_clone() const override final { return data ? new object_handler(*this) : nullptr; } - inline constexpr const std::type_info* get_type_info() const noexcept override final { return &typeid(T); } + inline constexpr const std::type_info* get_type_info() const noexcept override final { + if constexpr(requires { { sizeof(T) }; }) + return &typeid(T); + else return nullptr; + } T* data; }; diff --git a/src/main.cpp b/src/main.cpp index 3198e52..0475c13 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -101,6 +101,9 @@ static void use_moh() // Testing a hypothetical C interface extern "C" { struct c_struct_example; + struct opaque; + + void black_box(const opaque* __restrict__){} c_struct_example* cs_create(const c_struct_example* __restrict__ from); void cs_free(c_struct_example* p); @@ -113,6 +116,10 @@ static void use_c_struct() using type = c_struct_example; auto* allocp = cs_create(nullptr); + opaque_handle impossible{make_opaque_handle(nullptr, [](auto*, auto) noexcept { return static_cast(nullptr); })}; + + black_box(impossible); + const opaque_handle v{make_opaque_handle(allocp, [](auto* ptr, auto op) noexcept { if(ptr) { switch(op) {