Added `opaque_handle.is_type()`, added `make_opaque_object_handle<T, Args...>()` to construct an `opaque_handle` from a new object T in-place.

Fortune for opaque_handle's current commit: Half curse − 半凶
master
Avril 2 years ago
parent 2f32c1538f
commit 95e6cae2ce
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -20,6 +20,16 @@ struct opaque_handle_impl;
template<typename T>
constexpr inline bool opaque_is_incomplete = !requires { { sizeof(T) }; };
#define $impl _opaque__impl
namespace $impl {
template<typename T>
concept CompleteType = !opaque_is_incomplete<T>;
}
/// A handler that always fails, only really useful for `make_opaque_handle`s that start with null pointers or ones that do not need to be freed and cannot be copied.
template<typename T>
constexpr inline auto opaque_empty_handler = [] [[gnu::const, gnu::always_inline]] (auto*, auto) noexcept -> T* { return nullptr; };
struct opaque_handle_impl {
friend class opaque_handle;
@ -47,7 +57,7 @@ struct opaque_handle_impl {
template<typename T>
constexpr bool is_type() const noexcept {
if constexpr(requires { { sizeof(T) }; }) return is_type(typeid(T)); else return true;
if constexpr(requires { { sizeof(T) }; }) return is_type(typeid(T)); else return false; //XXX: Should this be `true`?
}
constexpr virtual bool is_type(const std::type_info& a) const noexcept {
if(const auto* p = get_type_info()) return *p == a;
@ -98,9 +108,21 @@ struct opaque_handle final {
/// WARNING: try_cast<T>(), where T is an incomplete type, will always succeed, which may lead to undefined behaviour. Make sure to account for this
template<typename T>
constexpr const T* try_cast() const noexcept { return _impl ? _impl->try_cast<T>() : nullptr; }
constexpr const T* try_cast() const noexcept { static_assert(!opaque_is_incomplete<T>, "try_cast<T> always returns nullptr when T is an incomplete type, use `ptr_cast()` if T may be incomplete, or `unsafe_cast()` is it is known to be incomplete");
return _impl ? _impl->try_cast<T>() : nullptr; }
template<typename T>
constexpr T* try_cast() noexcept { return _impl ? _impl->try_cast<T>() : nullptr; }
constexpr T* try_cast() noexcept { static_assert(!opaque_is_incomplete<T>, "try_cast<T> always returns nullptr when T is an incomplete type, use `ptr_cast()` if T may be incomplete, or `unsafe_cast()` if it is known to be incomplete.");
return _impl ? _impl->try_cast<T>() : nullptr; }
template<$impl::CompleteType T>
constexpr bool is_type() const noexcept {
return is_type(typeid(T));
}
constexpr bool is_type(const std::type_info& ty) const noexcept
{
return _impl ? _impl->is_type(ty) : false;
}
/// ptr_cast(): use try_cast() for complete types, and unsafe_cast() for incomplete types.
@ -115,14 +137,15 @@ struct opaque_handle final {
else return try_cast<T>();
}
template<typename T, cast_kind Kind = CAST_SAFE>
template<$impl::CompleteType T, cast_kind Kind = CAST_SAFE>
inline const T& cast() const {
if constexpr(Kind >= CAST_UNSAFE)
return *(unsafe_cast<T, (Kind == CAST_UNSAFE_REINTERPRET)>() ?: throw opaque_bad_cast{});
else
else { // CAST_SAFE
return *(try_cast<T>() ?: throw opaque_bad_cast{});
}
}
template<typename T, cast_kind Kind = CAST_SAFE>
template<$impl::CompleteType T, cast_kind Kind = CAST_SAFE>
inline T& cast() {
if constexpr(Kind >= CAST_UNSAFE)
return *(unsafe_cast<T, (Kind == CAST_UNSAFE_REINTERPRET)>() ?: throw opaque_bad_cast{});
@ -230,28 +253,32 @@ opaque_handle_object(T&&) -> opaque_handle_object<T>;
template<typename T>
constexpr inline opaque_handle make_opaque_object_handle(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>)
{
return opaque_handle{static_cast<opaque_handle_impl*>(new opaque_handle_object<T>(std::forward<T>(value)))};
return opaque_handle{static_cast<opaque_handle_impl*>(new opaque_handle_object<T>(std::move(value)))};
}
/// Make an opaque_handle from a newly constructed object `T`.
template<typename T, typename... Args> requires(std::is_constructible_v<T, Args...>)
constexpr inline opaque_handle make_opaque_object_handle(Args&&... ctor) noexcept(std::is_nothrow_constructible_v<T, Args...>)
{
return opaque_handle{static_cast<opaque_handle_impl*>(new opaque_handle_object<T>(std::forward<Args>(ctor)...))};
}
/// Operation for `OpaqueHandleFunc` functions.
enum class opaque_handle_operation
{
Clone,
Delete,
};
/// A function that handles the de-allocation and cloning (if supported) of an opaque_handle created with `make_opaque_handle`.
template<typename F, typename T>
concept OpaqueHandleFunc = std::is_invocable_v<F, T*, opaque_handle_operation>
&& std::is_convertible_v<std::invoke_result_t<F, T*, opaque_handle_operation>, T*>;
//TODO: Turn all local `constexpr is_nothrow` in the following functions into #define macros. The constexpr locals being used in a local class cause the compiler to segfault for some reason...
//XXX: TODO: `make_opaque_handle()`'s local classes cannot be casted back to `T*`, adding operator T* won't help here because they are static/dynamic casted. We need them to inherit `opaque_handle_impl`
/// Create and opaque_handle from a data pointer and a lambda which handles the copying (if possible) and deleting of the object
///
/// `handler` should be in the form: `auto* (T*, opaque_handle_operation) [noexcept]` and the reference must outlive the returned object. It should handle a `null` argument, and it can return `null`.
template<typename T, OpaqueHandleFunc<T> HandleF> // Note: This should be a lambda, or a free-function. it's lifetime must not end before the `opaque_handle` returned from this function does.
template<typename T, OpaqueHandleFunc<T> HandleF>
constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handler) noexcept
{
#define is_nothrow (std::is_nothrow_invocable_v<HandleF, T*, opaque_handle_operation>)
@ -270,7 +297,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handle
constexpr virtual ~object_handler() //noexcept(is_nothrow)
{
if(data) handler(data, opaque_handle_operation::Delete);
if(data) (void)handler(data, opaque_handle_operation::Delete);
}
inline constexpr void* as_raw_ptr() const noexcept override final
@ -316,7 +343,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data, HandleF&& handler) no
constexpr virtual ~object_handler() //noexcept(is_nothrow)
{
if(data) handler(data, opaque_handle_operation::Delete);
if(data) (void)handler(data, opaque_handle_operation::Delete);
}
inline constexpr void* as_raw_ptr() const noexcept override final
@ -364,7 +391,7 @@ constexpr inline opaque_handle make_opaque_handle(T* data) noexcept
constexpr virtual ~object_handler() //noexcept(is_nothrow)
{
if(data) Func(data, opaque_handle_operation::Delete);
if(data) (void)Func(data, opaque_handle_operation::Delete);
}
inline constexpr void* as_raw_ptr() const noexcept override final
@ -386,3 +413,4 @@ constexpr inline opaque_handle make_opaque_handle(T* data) noexcept
#undef $_oe_msg
#undef $_oe
#undef $impl

@ -116,8 +116,7 @@ static void use_c_struct()
using type = c_struct_example;
auto* allocp = cs_create(nullptr);
opaque_handle impossible{make_opaque_handle<opaque>(nullptr, [](auto*, auto) noexcept { return static_cast<opaque*>(nullptr); })};
opaque_handle impossible{make_opaque_handle<opaque, opaque_empty_handler<opaque>>(nullptr)};
black_box(impossible);
const opaque_handle v{make_opaque_handle<type>(allocp, [](auto* ptr, auto op) noexcept {
@ -144,8 +143,8 @@ static void use_c_struct()
int main()
{
const
opaque_handle v{make_opaque_object_handle(std::string{"Hello world"})};
opaque_handle v{make_opaque_object_handle<std::string>("Hello world")};
puts("Auto-downcasting to `const std::string&`:");
print(*v);
#if 1

Loading…
Cancel
Save