Added functional `opaque_object_handle<T>()` -> `opaque_handle`. Added auto-casting operators for `opaque_handle`. Fortune for opaque_handle's current commit: Blessing − 吉master
commit
70d92c60b1
@ -0,0 +1,3 @@
|
||||
*~
|
||||
*.o
|
||||
a.out
|
@ -0,0 +1,302 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
|
||||
#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<typename From>
|
||||
inline constexpr static opaque_handle from_object(T& ptr) noexcept
|
||||
{
|
||||
return opaque_handle{dynamic_cast<void*>(&ptr) ?: static_cast<void*>(&ptr)};
|
||||
}
|
||||
|
||||
inline constexpr void* ptr() const noexcept {
|
||||
return _ptr;
|
||||
}
|
||||
|
||||
template<typename To>
|
||||
inline constexpr T* downcast() const noexcept {
|
||||
return dynamic_cast<To*>(_ptr);
|
||||
}
|
||||
|
||||
template<typename To>
|
||||
inline constexpr downcast_unsafe() const noexcept {
|
||||
return static_cast<To*>(_ptr);
|
||||
}
|
||||
|
||||
template<typename To>
|
||||
inline T* ptr_as() { return std::bit_cast<To>(_ptr); }
|
||||
private:
|
||||
void* _ptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct opaque_handle_for : opaque_handle
|
||||
{
|
||||
constexpr opaque_handle_for(T&& value)
|
||||
: opaque_handle(opaque_handle::template from_object<T>(*new T(std::move(value)))){}
|
||||
constexpr virtual ~opaque_handle_for()
|
||||
{
|
||||
if(T* ptr = downcast_unsafe<T>()) {
|
||||
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<typename To>
|
||||
inline constexpr const To* try_cast() const noexcept { return static_cast<const To*>(as_raw_ptr()); }
|
||||
template<typename To>
|
||||
inline constexpr To* try_cast() noexcept { return static_cast<To*>(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<typename T>
|
||||
constexpr const T* try_cast() const noexcept { return static_cast<const T*>(as_ptr()); }
|
||||
template<typename T>
|
||||
constexpr T* try_cast() noexcept { return static_cast<T*>(as_ptr()); }
|
||||
|
||||
constexpr bool has_value() const noexcept { return bool(_impl); }
|
||||
constexpr explicit operator bool() const noexcept { return has_value(); }
|
||||
|
||||
template<typename T>
|
||||
constexpr const T* operator->() const noexcept { return try_cast<T>(); }
|
||||
template<typename T>
|
||||
constexpr T* operator->() noexcept { return try_cast<T>(); } //XXX: TODO: throw if try_cast<T>() 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<typename T>
|
||||
inline auto operator*() noexcept { return *(try_cast<T>() ?: throw opaque_bad_cast{}); }
|
||||
*/
|
||||
template<typename T>
|
||||
constexpr operator T*() noexcept { return try_cast<T>(); }
|
||||
template<typename T>
|
||||
constexpr operator const T*() const noexcept { return try_cast<T>(); }
|
||||
|
||||
private:
|
||||
struct _impl_deref_const {
|
||||
const opaque_handle& ptr;
|
||||
|
||||
template<typename T>
|
||||
inline operator const T&() && {
|
||||
return *(ptr.try_cast<T>() ?: throw opaque_bad_cast{});
|
||||
}
|
||||
template<typename T>
|
||||
inline operator const T&() const&& {
|
||||
return *(ptr.try_cast<T>() ?: throw opaque_bad_cast{});
|
||||
}
|
||||
};
|
||||
struct _impl_deref {
|
||||
opaque_handle& ptr;
|
||||
|
||||
template<typename T>
|
||||
inline operator T&() && {
|
||||
return *(ptr.try_cast<T>() ?: throw opaque_bad_cast{});
|
||||
}
|
||||
template<typename T>
|
||||
inline operator const T&() const&& {
|
||||
return *(ptr.try_cast<T>() ?: throw opaque_bad_cast{});
|
||||
}
|
||||
};
|
||||
opaque_handle_impl* _impl;
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
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<T>)
|
||||
: _obj(std::move(value)){}
|
||||
|
||||
|
||||
inline constexpr virtual ~opaque_handle_object() {}
|
||||
protected:
|
||||
inline constexpr void* as_raw_ptr() const noexcept override { return const_cast<void*>(static_cast<const void*>(&_obj) /*?: (static_cast<void*>(&_obj) ?: &_obj)*/); }
|
||||
inline constexpr opaque_handle_impl* try_clone() const override {
|
||||
if constexpr(std::is_copy_constructible_v<T>) {
|
||||
return new opaque_handle_object<T>{T{_obj}};
|
||||
} else return nullptr;
|
||||
}
|
||||
private:
|
||||
T _obj;
|
||||
};
|
||||
template<typename T>
|
||||
opaque_handle_object(T&&) -> opaque_handle_object<T>;
|
||||
|
||||
/// Make an opaque_handle from an object.
|
||||
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)))};
|
||||
}
|
||||
|
||||
enum class opaque_handle_operation
|
||||
{
|
||||
Clone,
|
||||
Delete,
|
||||
};
|
||||
|
||||
|
||||
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*>;
|
||||
|
||||
|
||||
/// 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.
|
||||
constexpr inline opaque_handle make_opaque_handle(T* data, const HandleF& handler) noexcept
|
||||
{
|
||||
constexpr bool is_nothrow = std::is_nothrow_invocable_v<HandleF, T*, opaque_handle_operation>;
|
||||
|
||||
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<typename T, OpaqueHandleFunc<T> HandleF>
|
||||
constexpr inline opaque_handle make_opaque_handle(T* data, HandleF&& handler) noexcept(std::is_nothrow_move_constructible_v<HandleF>)
|
||||
{
|
||||
constexpr bool is_nothrow = std::is_nothrow_invocable_v<HandleF, T*, opaque_handle_operation>;
|
||||
constexpr bool is_nothrow_ctor = std::is_nothrow_move_constructible_v<HandleF>;
|
||||
|
||||
struct object_handler
|
||||
{
|
||||
constexpr object_handler(const object_handler& c) noexcept(is_nothrow && std::is_nothrow_copy_constructible_v<HandleF>) requires(std::is_copy_constructible_v<HandleF>)
|
||||
: 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<HandleF>(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<typename T, OpaqueHandleFunc<T> 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<HandleF, T*, opaque_handle_operation>;
|
||||
|
||||
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});
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
|
||||
#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, "! <null>\n", 8);
|
||||
write(1, "\n", 1);
|
||||
#else
|
||||
if(__builtin_expect((sz = ptr.size()) > 0, true)) {
|
||||
printf("> %.*s\n", (int)sz, ptr.data());
|
||||
} else puts("! <null>\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;
|
||||
}
|
Loading…
Reference in new issue