Compare commits

..

3 Commits
master ... lean

Author SHA1 Message Date
Avril a5cf2c0fef
optional: Started impl for `null_optimise<Box<T>>::held_type` and `::convert_{to/from}_held()`s
1 year ago
Avril 65c6617627
Started `optional::null_optimise<Box<T>>` implementation. (Incomplete.)
1 year ago
Avril 6e11ff88a5
Added TODO: For lean build, complete error framework and work on ergonomic `constinit` ruleset definition (see TODO for info on possible implementation.)
1 year ago

@ -0,0 +1,9 @@
TODO: Remove all this bullshit like rng.h, keep pointer, current boxed, and util; keep the error stuff and get a consistent framework for it but then work on the ruleset, rule definitions, making it ergonomic and `constinit`, etc. The actual real reason this project exists...
Then add things like `iterator<T>` to use for parsing, and `argument_{input/output}_stream` for transformations, etc. See notes on phone for custom handlers as well. Use as much aggregate (POD) init as possible, e.g:
```
// with rule ctor for `<size_t DN>(char8_t, const char &(desc)[DN], kind, attribute = {})`
// ...
{ 'v', "Increase verbosity" , kind::Switch, { .allow_multuple = 3 }},
// ...
```

@ -20,7 +20,7 @@ namespace exopt::types { namespace boxed {
template<typename T>
constexpr std::unique_ptr<T> uniq_clone(std::unique_ptr<T> const& c) noexcept(std::is_nothrow_copy_constructible_v<T>) requires(std::is_copy_constructible_v<T>)
{ if(__builtin_expect(bool(c), true)) return std::make_unique<T>(*c); return nullptr; }
{ if(__builtin_expect(bool(c), true)) return std::make_unique<T>(*c); return { nullptr }; }
template<typename T>
constexpr std::unique_ptr<T> uniq_clone_unsafe(std::unique_ptr<T> const& c)
@ -62,14 +62,6 @@ namespace exopt::types { namespace boxed {
{ static_cast<std::decay_t<To> &&>(from) } -> std::convertible_to<To>;
};
/*struct ErasedTypeDeleter {
constexpr virtual ~ErasedTypeDeleter() = default;
constexpr ErasedTypeDeleter() noexcept = default;
constexpr virtual void apply_delete() = 0;
//TODO: See below...
};*/
template<polymorphic_castable<void> T>
constexpr auto uniq_erase_type(std::unique_ptr<T>&& from) noexcept {
struct deleter final /*: public ErasedTypeDeleter XXX <- Is this useful, for unerasing the type, maybe?*/ {
@ -267,6 +259,9 @@ namespace exopt::types { namespace boxed {
constexpr Box(util::exact_type<unsafe_t> auto u, std::unique_ptr<T>&& ptr) noexcept
: m_ptr(std::move(ptr)) { (void)u; }
constexpr Box(util::exact_type<unsafe_t> auto u, Box const& copy) noexcept
: m_ptr(uniq_clone(copy.as_unique(u))) {}
constexpr static Box<T> from_raw_ptr(unsafe_t u, std::unique_ptr<T>&& uniq) noexcept {
return Box{u, std::move(uniq)};
}

@ -11,13 +11,24 @@
#include "util.hh"
#define _EO_DEFEXCEPT_BASIC_(NAME, BASE, ...) class NAME : public ::exopt:: BASE { \
constexpr static inline auto MESSSAGE = (__VA_ARGS__); \
public: \
_EO_CLASS_DEFINE(NAME, constexpr, noexcept, virtual, =default); \
constexpr virtual std::string_view message() const noexcept override { return { MESSAGE }; } \
}
#define _EO_DEFEXCEPT_BASIC(NAME, MSG) _EO_DEFEXCEPT_BASIC_(NAME, Error, MSG)
#define _EO_DEFEXCEPT_TRACE_BASIC(NAME, MSG) _EO_DEFEXCEPT_BASIC_(NAME, TracedError, MSG)
namespace exopt {
// Report generated from an error. (virtual base, not abstract.)
class Report {
public:
virtual ~Report();
};
// This is propagated returned (base, abstract)
// This is base thrown (base, abstract)
class Error {
public:
constexpr Error(Error const&) = default;
@ -25,6 +36,9 @@ namespace exopt {
constexpr Error& operator=(Error &&) = default;
constexpr Error& operator=(Error const&) = default;
virtual types::Box<TracedError> into_traced(std::stacktrace&& trace) &&noexcept;// = std::stacktrace::current()) && noexcept;
inline typed::Box<TracedError> into_traced(std::stacktrace trace = std::stacktrace::current()) && noexcept
{ return std::move(*this).into_traced(std::move(trace)); }
constexpr virtual std::source_location const& location() const noexcept { return m_location; }
@ -32,8 +46,9 @@ namespace exopt {
constexpr virtual std::string_view message() const noexcept =0;
constexpr virtual types::Option<Error&> inner() noexcept { return { std::nullopt }; }
constexpr virtual types::MaybeOwned<Report> into_report() noexcept { /* TODO */ } //TODO: Maybe just use `std::optional<std::reference_wrapper<Error>>`? Or Box<Report&>?
//constexpr virtual types::Option<Error&> inner() noexcept { return { std::nullopt }; }
constexpr virtual types::Option<Error const&> inner() const noexcept { return { std::nullopt }; }
constexpr virtual types::MaybeOwned<Report> into_report() & noexcept { /* TODO */ } //TODO: Maybe just use `std::optional<std::reference_wrapper<Error>>`? Or Box<Report&>?
constexpr virtual types::MaybeOwned<Report> into_report() && noexcept { /* TODO: auto{*this}.into_report() */ }
constexpr virtual ~Error() = default;
@ -47,7 +62,7 @@ namespace exopt {
};
// This is propagated returned, along with a stack trace. (base, abstract)
class TracedError : Error {
class TracedError : public Error {
public:
TracedError(TracedError const &) = default;
TracedError(TracedError &&) = default;
@ -62,13 +77,27 @@ namespace exopt {
std::source_location loc = std::source_location::current()
, std::stacktrace trace = std::stacktrace::current()) noexcept
: Error(std::move(loc)), m_trace(std::move(trace)) {}
/*inline TracedError(
std::stacktrace&& trace,
, std::source_location loc = std::source_location::current()) noexcept
: Error(std::move(loc)), m_trace(std::move(trace)) {}*/
inline TracedError(
std::stacktrace&& trace,
, std::source_location&& loc) noexcept
: Error(std::move(loc)), m_trace(std::move(trace)) {}
private:
inline types::Box<TracedError> into_traced(std::stacktrace trace&&) &&noexcept override final// = std::stacktrace::current()) && noexcept;
{ return typed::Box<TracedError> { std::move(*this) }; } //TODO: use `boxed::is_boxed_value()` to check if *this is boxed... Uhh, actually... XXX: No that doesn't work, since `Box<T>` doesn't make `T` visible to `boxed::ObjectBase`.... Eh...
inline typed::Box<TracedError> into_traced(std::stacktrace trace = std::stacktrace::current()) && noexcept override final
{ return typed::Box<TracedError> { std::move(*this) }; }
std::stacktrace m_trace; //TODO: Maybe box this? idk how big it is...
};
#if 0
// This is thrown. (virtual base, abstract)
class Panic : /*virtual?? I think it should be XXX*/ Error {
//TODO: How to make a `constexpr` deduced panic that has stack-trace at runtime only? Is it needed? Hnm...
};
//TODO: Fatal(string) : public virtual Panic
#endif
}

@ -22,36 +22,17 @@ extern "C" {
#define _exopt__internal __attribute__((visibility("internal")))
#define _exopt__readonly __attribute__((__pure__))
#define _exopt__pure __attribute__((__const__))
#ifdef DEBUG
# define _exopt__DEBUG 1
#else
# define _exopt__DEBUG 0
#endif
#ifdef RELEASE
# define _exopt__RELEASE 1
#else
# define _exopt__RELEASE 0
#endif
#ifdef _EO__OPT_OLD_ASSUME
# define _EO_ASSUME(X) ({ if(! (X)) __builtin_unreachable(); })
#endif
#define _EO_FIXED_INT(B) _BitInt(B)
#define _EO_GNU_ATTR(...) __attribute__((__VA_ARGS__))
#if _EO(cpp)
# define _EO_ATTR(...) [[__VA_ARGS__]]
# define _EO_SHARES_LAYOUT(T, U) (sizeof(T) == sizeof(U) && alignof(T) == alignof(U))
# ifndef _EO_ASSUME
# define _EO_ASSUME(X) [[gnu::assume(X)]]
# endif
# define _exopt__restrict __restrict__
#else /* C */
# define _EO_ATTR(...) __attribute__((__VA_ARGS__))
# define _EO_SHARES_LAYOUT(T, U) (sizeof(T) == sizeof(U) && alignof(T) == alignof(U))
# define _EO_SHARES_LAYOUT(T, U) (sizeof(T) == sizeof(U) && _Alignof(T) == _Alignof(U))
#else
# ifndef _EO_ASSUME
# define _EO_ASSUME(X) __attribute__((assume(X)))
# endif

@ -6,6 +6,8 @@
#include "pointer.h"
#include "exopt.h"
/// A better std::optional
namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
template<typename T>
@ -21,6 +23,8 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
constexpr static inline auto sentinel = nullptr;
};
template<typename T>
struct null_optimise<std::reference_wrapper<T> > { constexpr static inline bool value = true;
using held_type = T*;
@ -29,7 +33,89 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
constexpr static decltype(auto) convert_from_held(std::add_rvalue_reference_t<held_type> t) noexcept { return std::ref<T>(*t); }
constexpr static inline auto sentinel = nullptr;
};
};
template<typename T>
class null_optimise<boxed::Box<T> > {
using boxed::Box;
using box_t = Box<T>;
class /*XXX: Shouldn't be needed alignas(box_t)*/ invariant : box_t {
//TODO: See phone notes.
using box_t::UNSAFE;
public:
constexpr box_t& operator*() & noexcept { return *static_assert<box_t *>(this); }
constexpr box_t const& operator*() const& noexcept { return *static_assert<box_t const*>(this); }
constexpr box_t&& operator*() && noexcept { return std::move(*static_assert<box_t *>(this)); }
constexpr box_t const&& operator*() const&& noexcept { return std::move(*static_assert<box_t const*>(this)); }
constexpr static invariant&& back_conv(box_t&& b) noexcept { return static_cast<invariant&&>(b); }
constexpr static invariant const& back_conv(box_t const& b) noexcept { return static_cast<invariant const&>(b); }
constexpr static invariant& back_conv(box_t& b) noexcept { return static_cast<invariant&>(b); }
constexpr static invariant const&& back_conv(box_t const&& b) noexcept { return static_cast<invariant const&&>(b); }
constexpr invariant(box_t&& m) noexcept
: box_t(UNSAFE, std::move(std::move(m).as_unique(UNSAFE))) {}
constexpr invariant(box_t const& m) noexcept
: box_t(UNSAFE, m) {}
constexpr invariant(std::unique_ptr<T>&& m) noexcept
: box_t(UNSAFE, std::move(m)) {}
constexpr invariant(std::nullptr_t) noexcept
: box_t(UNSAFE, std::unique_ptr<T> { nullptr } ) {}
constexpr friend auto operator<=>(invariant const& a, invariant const& b) noexcept { return (*a).as_unsafe_ptr(UNSAFE) <=> (*b).as_unsafe_ptr(UNSAFE); }
constexpr friend auto operator<=>(invariant const& a, box_t const& b) noexcept { return (*a).as_unsafe_ptr(UNSAFE) <=> b.as_unsafe_ptr(UNSAFE); }
constexpr friend auto operator<=>(invariant const& a, T const* p) noexcept { return (*a).as_unsafe_ptr(UNSAFE) <=> p; }
constexpr friend auto operator<=>(invariant const& a, std::nullptr_t) noexcept { return (*a).as_unsafe_ptr(UNSAFE) <=> nullptr; }
constexpr invariant& operator=(box_t &&) noexcept = delete;
constexpr invariant& operator=(box_t const&) = delete;
constexpr invariant& operator=(invariant &&) noexcept = default;
constexpr invariant& operator=(invariant const&) = default;
constexpr invariant& operator=(std::nullptr_t)
{ box_t::as_unique(UNSAFE).reset(); return *this; }
constexpr invariant& operator=(T* const&& p)
{ box_t::as_unique(UNSAFE).reset(p); return *this; }
constexpr invariant(invariant&& m) noexcept
: invariant( std::move((*std::move(m)).as_unique(UNSAFE)) ) {}
constexpr invariant(invariant const& m) noexcept
: box_t(UNSAFE, (*m).as_unique(UNSAFE)) {}
constexpr ~invariant() = default;
};
static_assert(util::shares_layout<invariant, box_t>, "invariant (held_type) does not share layout with viewed type (Box<T>)");
public:
typedef invariant held_type; // invariant ^: Held internal type.
using type = box_t; // Box<T>: API seen & operated on type
constexpr static decltype(auto) convert_to_held(type ty) noexcept { return held_type{std::move(ty)}; }
constexpr static decltype(auto) convert_to_held(type& ty) noexcept { return held_type::back_conv(ty); }
constexpr static decltype(auto) convert_to_held(type&& ty) noexcept { return held_type::back_conv(std::move(ty)); }
constexpr static decltype(auto) convert_to_held(type const& ty) noexcept { return held_type::back_conv(ty); }
constexpr static decltype(auto) convert_to_held(type const&& ty) noexcept { return held_type::back_conv(std::move(ty)); }
constexpr static type& convert_from_held(held_type& ty) noexcept { return *ty; }
constexpr static type const&& convert_from_held(held_type const&& ty) noexcept { return std::move(*std::move(ty)); }
constexpr static type&& convert_from_held(held_type&& ty) noexcept { return std::move(*std::move(ty)); }
constexpr static type const& convert_from_held(held_type const& ty) noexcept { return *ty; }
//TODO: See phone notes.
constexpr static inline auto sentinel = nullptr;
};
#ifdef DEBUG /* We don't want to instantiate `null_optimise<>` *at all* if we don't have to outside of actual real use in `Option<T>`. This check is for development only since Option<Box<T>> is a bit of an awkward developmental case.
Save compile time for release builds, the `static_assert` inside the null-opt partial spec for `..::invariant`'s layout will be checked if/when `Option<Box<T>>` is ever instantiated. This check is the same just assuring it's valid for *if* Option<Box<T>> is ever instantiated */
static_assert(!requires(requires{ typename boxed::Box<int>; }) or // Skip check if `boxed.h` is not included.
requires(requires(null_optimise<boxed::Box<int>>::held_type const& held) {
{ static_cast<boxed::Box<int> const&>(held) };
}), "Nullopt held_type for Box<T> either has a unneeded and unwanted generated vtable or has invalid alignment, check into that");
#endif
template<typename T>
struct null_optimise<ptr::Unique<T> > { constexpr static inline bool value = true;

@ -0,0 +1,30 @@
#include <stdexcept>
#include <error.hh>
using namespace exopt::types;
namespace exopt {
Box<TracedError> Error::into_traced(std::stacktrace&& trace) &&noexcept {
struct DynamicTracedError final : TracedError {
_EO_CTORS_BASIC_DEFAULT(DynamicTracedError, _EO_NOTHING, noexcept);
virtual ~DynamicTracedError() noexcept = default;
DynamicTracedError(Error&& self, std::stacktrace&& trace) noexcept
: TracedError(std::move(trace), std::move(self.m_location))
, m_error(std::move(self)) {}
std::string_view message() const noexcept override { return m_error->message(); }
Option<Error const&> inner() const noexcept override { return Option<Error const&>(*m_error); }//{ return Option<Error const&>::from_pointer(std::as_const(m_error.get())); }
MaybeOwned<Report> into_report() && noexcept override { return std::move(*m_error).into_report(); }
MaybeOwned<Report> into_report() & noexcept override { return m_error->into_report(); }
private:
Box<Error> m_error;
};
#define ERR DynamicTracedError{std::move(*this), std::move(trace)}
return Box<TracedError>::template new_dynamic<DynamicTracedError, false>(ERR);
#undef ERR
}
}
Loading…
Cancel
Save