You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libexopt/include/array.h

124 lines
4.6 KiB

#pragma once
#include <array>
#include <utility>
#include <memory>
#include "pointer.h"
#include "util.hh"
#include "exopt.h"
namespace exopt::types {
namespace polyarray [[gnu::visibility("internal")]] {
template<typename T, size_t N>
struct SizedArray;
struct UnsizedAccessor {
constexpr virtual ~UnsizedAccessor() = default;
constexpr virtual T* data() const noexcept =0;
};
template<typename T, size_t N>
struct SizedAccessor : virtual UnsizedAccessor {
//using UnsizedAccessor::UnsizedAccessor;
constexpr virtual ~SizedAccessor() = default;
constexpr T* data() const noexcept override final { return raw()->data(); }
[[gnu::returns_nonnull]]
constexpr virtual std::array<T, N>* raw() noexcept =0;
[[gnu::returns_nonnull]]
constexpr virtual std::array<T, N> const* raw() const noexcept =0;
constexpr virtual std::array<T, N>& array() & noexcept { return *raw(); }
constexpr virtual std::array<T, N> const& array() const& noexcept { return *raw(); }
constexpr virtual std::array<T, N>&& array() && noexcept { return std::move(*raw()); }
constexpr virtual std::array<T, N> const&& array() const&& noexcept { return std::move(*raw()); }
};
/// An `std::array<T>` with unknown compile-time size.
template<typename T>
class PolyArray : public virtual UnsizedAccessor {
public:
constexpr PolyArray() noexcept = default;
constexpr virtual ~PolyArray() = default;
using UnsizedAccessor::data; //constexpr virtual T* data() const noexcept =0; Inherited from UnsizedAccessor
constexpr virtual size_t size() const noexcept =0;
//TODO: std::array-like accessors based on `data()..size()`
template<size_t N>
constexpr std::array<T, N>* try_downcast() noexcept {
if consteval {
static_assert(size() == N, "Invalid downcast array size");
}
if (size() == N) { return static_cast<SizedAccessor<T, N>*>(accessor())->raw(); } //reinterpret_cast<std::array<T, N>*>(unsafe_data()); }
return nullptr;
}
template<size_t N>
constexpr std::array<T, N>&& downcast() && {
if(auto* nar = try_downcast()) {
return std::move(*nar);
} else throw_invalid_size();
}
constexpr bool empty() const noexcept { return !size(); }
protected:
[[noreturn, gnu::noinline, gnu::cold]]
constexpr void throw_invalid_size() {
std::terminate(); //TODO: throw `poly_array_size_exception`
}
private:
[[gnu::returns_nonnull]]
constexpr UnsizedAccessor* accessor() noexcept { return static_cast<UnsizedAccessor*>(this); }
[[gnu::returns_nonnull]]
constexpr const UnsizedAccessor* accessor() const noexcept { return static_cast<UnsizedAccessor const*>(this); }
template<size_t N>
[[gnu::returns_nonnull]]
constexpr SizedAccessor<T, N>* unsafe_cast() noexcept { return static_cast<SizedAccessor<T, N>*>(
static_cast<UnsizedAccessor*>(this)); }
};
template<typename T, size_t N>
struct SizedArray : virtual /*XXX: Needed? since both non-virtual bases of this class have virtual base UnsizedAccessor?*/ SizedAccessor<T, N>
, public PolyArray<T> {
[[gnu::const]]
constexpr size_t size() const noexcept override final { return N; }
//[[gnu::const, gnu::returns_nonnull]] // Unneeded, SizedAccessor implements it as raw()->data()
//constexpr T* data() const noexcept override final { return m_data.data(); }
constexpr SizedArray(std::array<T, N>&& m) noexcept
: m_data(std::move(m)) {}
//TODO: ctors, assign, etc.
constexpr virtual ~SizedArray() = default;
//TODO: operator*() decltype(auto) forwards from SizedAccessor::array()
protected:
[[gnu::returns_nonnull]]
constexpr std::array<T, N>* raw() noexcept override final { return std::addressof(m_data); }
[[gnu::returns_nonnull]]
constexpr std::array<T, N> const* raw() const noexcept override final { return std::addressof(m_data); }
private:
std::array<T, N> m_data;
};
template<typename T, size_t N>
constexpr std::unique_ptr<PolyArray<T>> make_unsized(std::array<T, N>&& array) noexcept {
auto sub = std::make_unique<SizedArray<T, N>>(std::move(array));
return std::unique_ptr<PolyArray<T>> { static_cast<PolyArray<T>*>(sub.release()) };
}
template<typename T, size_t N>
constexpr std::array<T, N>&& /*XXX: <- Is returning an rvalue-ref here acceptible? When is `array` dropped here? Does the caller actuall drop it or the std::move() in here...? We aren't actually assigning the move to anything at all, we're just working through rvalue-refs, so it *should* be fine...??? Fuck this language honestly... */ make_sized(std::unique_ptr<PolyArray<T>>&& array) {
return (*std::move(array)).downcast<T, N>();
}
}
}