commit 70d92c60b1bc2da030ad2cff886eaace00596a22 Author: Avril Date: Sat Mar 12 16:44:01 2022 +0000 Initial commit Added functional `opaque_object_handle()` -> `opaque_handle`. Added auto-casting operators for `opaque_handle`. Fortune for opaque_handle's current commit: Blessing − 吉 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8919ad2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +*.o +a.out diff --git a/include/opaque.hh b/include/opaque.hh new file mode 100644 index 0000000..1925892 --- /dev/null +++ b/include/opaque.hh @@ -0,0 +1,302 @@ +#pragma once + +#include +#include + +#if 0 //XXX: This doesn't work, we need `opaque_handle` to not be a template... +namespace _opaque_impl { +struct opaque_handle { + + virtual constexpr ~opaque_handle(); +protected: + inline constexpr explicit opaque_handle(void* data) noexcept : _ptr(data){} + template + inline constexpr static opaque_handle from_object(T& ptr) noexcept + { + return opaque_handle{dynamic_cast(&ptr) ?: static_cast(&ptr)}; + } + + inline constexpr void* ptr() const noexcept { + return _ptr; + } + + template + inline constexpr T* downcast() const noexcept { + return dynamic_cast(_ptr); + } + + template + inline constexpr downcast_unsafe() const noexcept { + return static_cast(_ptr); + } + + template + inline T* ptr_as() { return std::bit_cast(_ptr); } +private: + void* _ptr; +}; + +template +struct opaque_handle_for : opaque_handle +{ + constexpr opaque_handle_for(T&& value) + : opaque_handle(opaque_handle::template from_object(*new T(std::move(value)))){} + constexpr virtual ~opaque_handle_for() + { + if(T* ptr = downcast_unsafe()) { + delete ptr; + } + } +} +} + +struct opaque_handle +{ +private: + +}; +#endif + +struct opaque_not_copyable : std::exception{}; +struct opaque_handle_moved : std::exception{}; +struct opaque_bad_cast : std::exception{}; + +struct opaque_handle; +struct opaque_handle_impl; + +struct opaque_handle_impl { + friend class opaque_handle; + + virtual constexpr ~opaque_handle_impl() noexcept {} + + template + inline constexpr const To* try_cast() const noexcept { return static_cast(as_raw_ptr()); } + template + inline constexpr To* try_cast() noexcept { return static_cast(as_raw_ptr()); } + + +protected: + constexpr virtual void* as_raw_ptr() const noexcept = 0; + constexpr virtual opaque_handle_impl* try_clone() const = 0; + + inline virtual opaque_handle_impl& clone() const { + if(auto* np = try_clone()) return *np; + throw opaque_not_copyable{}; + } + + constexpr opaque_handle_impl() noexcept{} +}; + +struct opaque_handle final { + constexpr explicit opaque_handle(opaque_handle_impl* ptr) noexcept : _impl(ptr){} + + constexpr opaque_handle(opaque_handle&& move) noexcept + : _impl(std::exchange(move._impl, nullptr)){} + inline opaque_handle(const opaque_handle& copy) + : _impl(copy._impl + ? std::addressof(copy._impl->clone()) //(copy._impl->try_clone() ?: throw opaque_not_copyable{}) + : nullptr){} + + constexpr ~opaque_handle() + { + if(_impl) delete _impl; + } + + constexpr const void* as_ptr() const noexcept { return _impl ? _impl->as_raw_ptr() : nullptr; } + constexpr void* as_ptr() noexcept { return _impl ? _impl->as_raw_ptr() : nullptr; } + + template + constexpr const T* try_cast() const noexcept { return static_cast(as_ptr()); } + template + constexpr T* try_cast() noexcept { return static_cast(as_ptr()); } + + constexpr bool has_value() const noexcept { return bool(_impl); } + constexpr explicit operator bool() const noexcept { return has_value(); } + + template + constexpr const T* operator->() const noexcept { return try_cast(); } + template + constexpr T* operator->() noexcept { return try_cast(); } //XXX: TODO: throw if try_cast() is null and T is not `void`. + + inline auto operator*() const noexcept { + return _impl_deref_const{*this}; + } + inline auto operator*() noexcept { + return _impl_deref{*this}; + } +/* + template + inline auto operator*() noexcept { return *(try_cast() ?: throw opaque_bad_cast{}); } +*/ + template + constexpr operator T*() noexcept { return try_cast(); } + template + constexpr operator const T*() const noexcept { return try_cast(); } + +private: + struct _impl_deref_const { + const opaque_handle& ptr; + + template + inline operator const T&() && { + return *(ptr.try_cast() ?: throw opaque_bad_cast{}); + } + template + inline operator const T&() const&& { + return *(ptr.try_cast() ?: throw opaque_bad_cast{}); + } + }; + struct _impl_deref { + opaque_handle& ptr; + + template + inline operator T&() && { + return *(ptr.try_cast() ?: throw opaque_bad_cast{}); + } + template + inline operator const T&() const&& { + return *(ptr.try_cast() ?: throw opaque_bad_cast{}); + } + }; + opaque_handle_impl* _impl; +}; + + +template +struct opaque_handle_object : public opaque_handle_impl +{ + friend class opaque_handle; + + inline constexpr opaque_handle_object(T&& value) noexcept(std::is_nothrow_move_constructible_v) + : _obj(std::move(value)){} + + + inline constexpr virtual ~opaque_handle_object() {} +protected: + inline constexpr void* as_raw_ptr() const noexcept override { return const_cast(static_cast(&_obj) /*?: (static_cast(&_obj) ?: &_obj)*/); } + inline constexpr opaque_handle_impl* try_clone() const override { + if constexpr(std::is_copy_constructible_v) { + return new opaque_handle_object{T{_obj}}; + } else return nullptr; + } +private: + T _obj; +}; +template +opaque_handle_object(T&&) -> opaque_handle_object; + +/// Make an opaque_handle from an object. +template +constexpr inline opaque_handle make_opaque_object_handle(T&& value) noexcept(std::is_nothrow_move_constructible_v) +{ + return opaque_handle{static_cast(new opaque_handle_object(std::forward(value)))}; +} + +enum class opaque_handle_operation +{ + Clone, + Delete, +}; + + +template +concept OpaqueHandleFunc = std::is_invocable_v + && std::is_convertible_v, T*>; + + +/// 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 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. +constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handler) noexcept +{ + constexpr bool is_nothrow = std::is_nothrow_invocable_v; + + struct object_handler + { + constexpr object_handler(const object_handler& c) noexcept(is_nothrow) + : data(c.handler(data, opaque_handle_operation::Clone)) + , handler(c.handler){} + constexpr object_handler(object_handler&& m) noexcept + : data(std::exchange(m.data, nullptr)) + , handler(m.handler){} + constexpr object_handler(T* data, const HandleF& handler) noexcept + : data(data) + , handler(handler){} + + constexpr /*virtual*/ ~object_handler() noexcept(is_nothrow) + { + if(data) handler(data, opaque_handle_operation::Delete); + } + + T* data; + const HandleF& handler; + }; + + return make_opaque_object_handle(object_handler{data, handler}); +} + +/// Create and opaque_handle from a data pointer and a move-constructible functor which handles the copying (if possible) and deleting of the object. +/// +/// `handler` should be in the form: `auto* (T*, opaque_handle_operation) [noexcept]`. Its lifetime is managed by the returned opaque_handle +template HandleF> +constexpr inline opaque_handle make_opaque_handle(T* data, HandleF&& handler) noexcept(std::is_nothrow_move_constructible_v) +{ + constexpr bool is_nothrow = std::is_nothrow_invocable_v; + constexpr bool is_nothrow_ctor = std::is_nothrow_move_constructible_v; + + struct object_handler + { + constexpr object_handler(const object_handler& c) noexcept(is_nothrow && std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v) + : data(c.handler(data, opaque_handle_operation::Clone)) + , handler(c.handler){} + constexpr object_handler(object_handler&& m) noexcept + : data(std::exchange(m.data, nullptr)) + , handler(std::move(m.handler)){} + + constexpr object_handler(T* data, HandleF&& handler) noexcept(is_nothrow_ctor) + : data(data) + , handler(std::move(handler)){} + + constexpr /*virtual*/ ~object_handler() noexcept(is_nothrow) + { + if(data) handler(data, opaque_handle_operation::Delete); + } + + T* data; + HandleF handler; + }; + + return make_opaque_object_handle(object_handler{data, std::forward(handler)}); +} + +/// Create and opaque_handle from a data pointer and a constexpr handler funcion template param (e.g. a lambda) +/// +/// `Func` should be in the form: `auto* (T*, opaque_handle_operation) [noexcept]`. It must be a constant expression. +template auto Func> +constexpr inline opaque_handle make_opaque_handle(T* data) noexcept +{ + using HandleF = decltype(Func); + constexpr bool is_nothrow = std::is_nothrow_invocable_v; + + struct object_handler + { + constexpr object_handler(const object_handler& c) noexcept(is_nothrow) + : data(Func(data, opaque_handle_operation::Clone)){} + + constexpr object_handler(object_handler&& m) noexcept + : data(std::exchange(m.data, nullptr)){} + + constexpr object_handler(T* data) noexcept + : data(data){} + + constexpr /*virtual*/ ~object_handler() noexcept(is_nothrow) + { + if(data) Func(data, opaque_handle_operation::Delete); + } + + T* data; + }; + + return make_opaque_object_handle(object_handler{data}); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..43e03be --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include "../include/opaque.hh" + + +static void print_view(std::string_view ptr) +{ + //std::string_view ptr{string}; + + size_t sz; +#if DIRECT_WRITE + if(__builtin_expect((sz = ptr.size()) > 0, true)) { + write(1, "> ", 2); + write(1, ptr.data(), sz); + } else write(1, "! \n", 8); + write(1, "\n", 1); +#else + if(__builtin_expect((sz = ptr.size()) > 0, true)) { + printf("> %.*s\n", (int)sz, ptr.data()); + } else puts("! \n"); +#endif +} +static inline void print(const std::string& string) +{ + std::string_view sv{string}; + return print_view(sv); +} + +int main() +{ + const opaque_handle v{make_opaque_object_handle(std::string{"Hello world"})}; + print(*v); //XXX: Why does converting it to string_view here break it? + return 0; +}