Skip to content

Instantly share code, notes, and snippets.

@00001H
Last active September 18, 2023 13:33
Show Gist options
  • Save 00001H/e5969595791d6ff060d91a95bbe59f2b to your computer and use it in GitHub Desktop.
Save 00001H/e5969595791d6ff060d91a95bbe59f2b to your computer and use it in GitHub Desktop.
A lightweight C++ enhancement library
#ifndef SCP_CPPP
#define SCP_CPPP
#include<unordered_map>
#include<unordered_set>
#include<string_view>
#include<type_traits>
#include<functional>
#include<filesystem>
#include<exception>
#include<optional>
#include<iostream>
#include<typeinfo>
#include<fstream>
#include<cstring>
#include<utility>
#include<cassert>
#include<string>
#include<limits>
#include<vector>
#include<memory>
#include<bitset>
#include<cmath>
#include<tuple>
#include<deque>
#include<span>
#include<any>
#if (defined CPPP_REMOVE_ASSERT_MACRO) || (defined CPPP_ASSERT_FUN)
#undef assert
#ifdef CPPP_ASSERT_FUN
class assertion_failed : public std::logic_error{using std::logic_error::logic_error;};
inline void assert(bool cond,const char8_t* msg=u8""s){
if(!cond){
throw assertion_failed(msg);
}
}
#endif
#endif
namespace cppp{
using namespace std::literals;
inline namespace strconcat{
inline std::u8string operator+(std::u8string x,std::u8string_view y){
x.append(y);
return x;
}
inline std::u8string operator+(std::u8string_view x,const std::u8string& y){
std::u8string dup{x};
dup += y;
return dup;
}
inline std::u8string operator+(std::u8string_view x,std::u8string_view y){
std::u8string dup{x};
dup += y;
return dup;
}
}
inline namespace fsint{
using std::uint8_t;
using std::uint16_t;
using std::uint32_t;
using std::uint64_t;
using std::int8_t;
using std::int16_t;
using std::int32_t;
using std::int64_t;
}
inline std::u8string copy_as_u8(std::string_view sv){
std::u8string o;
for(char8_t c : sv){
o.push_back(c);
}
return o;
}
inline std::u8string_view view_as_u8(std::string_view sv)
#ifdef CPPP_USE_UB_REINTERPRET_CAST
{
return {reinterpret_cast<const char8_t*>(sv.data()),sv.length()};
}
#else
= delete;
#endif
inline std::string_view view_as_plain(std::u8string_view sv){
return {reinterpret_cast<const char*>(sv.data()),sv.length()};
}
inline std::string copy_as_plain(std::u8string_view sv){
std::string o;
for(const char c : sv){
o.push_back(c);
}
return o;
}
template<typename T>
inline std::u8string to_u8string(T&& t){
return copy_as_u8(std::to_string(t));
}
template<std::derived_from<std::exception> T>
class u8_error : public T{
public:
u8_error(std::u8string_view sv) : T(copy_as_plain(sv)){}
std::u8string u8what() const{
return copy_as_u8(static_cast<const T*>(this)->what());
}
};
using u8_logic_error = u8_error<std::logic_error>;
using u8_runtime_error = u8_error<std::runtime_error>;
class assume_u8_ostream{
public:
using value_type = std::ostream;
using char_type = value_type::char_type;
using traits_type = value_type::traits_type;
private:
value_type& os;
using _bxx_arg = std::basic_ios<char_type,traits_type>;
public:
assume_u8_ostream(value_type& os) : os(os){}
template<typename T>
assume_u8_ostream& operator<<(T&& x){
using _plain_T = std::remove_cvref_t<T>;
if constexpr(std::is_same_v<_plain_T,char8_t>){
os << static_cast<char>(x);
}else if constexpr(std::is_same_v<_plain_T,std::u8string>||std::is_same_v<_plain_T,std::u8string_view>){
os << std::string_view(
reinterpret_cast<const char*>(x.data()),
x.length()
);
}else{
os << std::forward<T>(x);
}
return *this;
}
assume_u8_ostream& operator<<(std::ios_base&(*x)(std::ios_base&)){
os << x;
return *this;
}
assume_u8_ostream& operator<<(_bxx_arg&(*x)(_bxx_arg&)){
os << x;
return *this;
}
assume_u8_ostream& operator<<(value_type&(*x)(value_type&)){
os << x;
return *this;
}
value_type& operator*(){
return os;
}
value_type* operator->(){
return &os;
}
};
inline assume_u8_ostream fcout{std::cout};
inline assume_u8_ostream fclog{std::clog};
inline assume_u8_ostream fcerr{std::cerr};
template<typename T, typename Ret, typename ...Args>
concept function_type = requires(std::remove_cvref_t<T> t){
{t(std::declval<Args>()...)} -> std::convertible_to<Ret>;
};
template<typename T>
concept supports_std_tostring = requires(T t){
{cppp::to_u8string(t)} -> std::convertible_to<std::u8string>;
};
template<typename T,typename U>
concept maybe_ref_of = std::same_as<std::remove_reference_t<T>,U>;
template<typename T,template<typename...> typename E>
struct is_instantiation : std::false_type{};
template<template<typename...> typename E,typename ...A>
struct is_instantiation<E<A...>,E> : std::true_type{};
template<typename T,template<typename...> typename E>
constexpr bool is_instantiation_v = is_instantiation<T,E>::value;
template<typename T,template<typename...> typename E>
concept instantiation_of = is_instantiation_v<T,E>;
template<typename T,typename ...A>
std::unique_ptr<T> unew(A&& ...a){
return std::unique_ptr<T>(new T(std::forward<A>(a)...));
}
template<typename T,const bool cond, T x, T y>
struct ternary{
constexpr static T value = x;
};
template<typename T,const T x, T y>
struct ternary<T,false,x,y>{
constexpr static T value = y;
};
template<typename T,const bool cond,T x,T y>
constexpr T ternary_v = ternary<T,cond,x,y>::value;
template<typename T,typename ...A>
struct first{
using type = T;
};
template<typename T,typename ...A>
using first_t = first<A...>::type;
template<typename T,typename>
struct match_cv{
using type = std::remove_cv_t<T>;
};
template<typename T,typename M>
struct match_cv<T,const M>{
using type = const std::remove_volatile_t<T>;
};
template<typename T,typename M>
struct match_cv<T,volatile M>{
using type = volatile std::remove_const_t<T>;
};
template<typename T,typename M>
struct match_cv<T,const volatile M>{
using type = const volatile T;
};
template<typename T,typename M>
using match_cv_t = match_cv<T,M>::type;
template<typename T,typename R>
concept issame_ignoring_cv = std::same_as<std::remove_cv_t<T>,std::remove_cv_t<R>>;
template<typename T>
concept std_iterable = requires(std::remove_cvref_t<T> t, const std::remove_cvref_t<T> c){
{*(t.begin())} -> std::convertible_to<typename T::value_type&>;
{*(t.end())} -> std::convertible_to<typename T::value_type&>;
{*(c.begin())} -> std::convertible_to<const typename T::value_type&>;
{*(c.end())} -> std::convertible_to<const typename T::value_type&>;
{*(c.cbegin())} -> std::convertible_to<const typename T::value_type&>;
{*(c.cend())} -> std::convertible_to<const typename T::value_type&>;
};
template<typename T>
concept ra_seq_container = std_iterable<T>
&& requires(std::remove_cvref_t<T> t, const std::remove_cvref_t<T> c,const size_t i){
{t[i]} -> std::convertible_to<typename T::value_type&>;
{t.at(i)} -> std::convertible_to<typename T::value_type&>;
{c[i]} -> std::convertible_to<const typename T::value_type&>;
{c.at(i)} -> std::convertible_to<const typename T::value_type&>;
{t.size()} -> std::convertible_to<const typename T::size_type>;
};
template<typename T>
concept associative_container = requires(std::remove_cvref_t<T> t, const std::remove_volatile_t<std::remove_reference_t<T>> c, typename T::key_type ky, typename T::mapped_type vl){
{t[ky]} -> std::convertible_to<typename T::mapped_type&>;
{t.at(ky)} -> std::convertible_to<typename T::mapped_type&>;
{c.at(ky)} -> std::convertible_to<const typename T::mapped_type&>;
};
template<typename T, typename ...Args>
concept aret_fun = requires(T t){
{t(std::declval<Args>()...)};
};
template<ra_seq_container Vec,
function_type<bool,const typename Vec::value_type&,size_t> fun>
inline void stdForEach(const Vec& v,const fun eachFunc){
size_t i=0uz;
for(auto& e : v){
if(eachFunc(e,i++))return;
}
}
template<ra_seq_container Vec,
function_type<bool,typename Vec::value_type&,size_t> fun>
inline void stdForEach(Vec& v,const fun eachFunc){
size_t i=0uz;
for(auto& e : v){
if(eachFunc(e,i++))return;
}
}
template<associative_container Map,
function_type<bool,const typename Map::key_type&,const typename Map::mapped_type&> fun>
inline void stdForEach(const Map& m,const fun eachFunc){
for(auto& [k,v] : m){
if(eachFunc(k,v))return;
}
}
template<associative_container Map,
function_type<bool,const typename Map::key_type&,typename Map::mapped_type&> fun>
inline void stdForEach(Map& m,const fun eachFunc){
for(auto& [k,v] : m){
if(eachFunc(k,v))return;
}
}
template<associative_container M0, associative_container M1>
inline void combine_dict(M0& dst, const M1& src){
for(const auto& e : src){
dst.insert(e);
}
}
template<class SUB,typename SUPER>
inline bool isinstanceof(const SUPER* instance) requires(std::derived_from<SUB,SUPER>){
return dynamic_cast<const SUB*>(instance)!=nullptr;
}
template<class SUB,typename SUPER>
inline bool isinstanceof(const SUPER& instance) requires(std::derived_from<SUB,SUPER>){
return isinstanceof<SUB>(&instance);
}
template<class SUB,typename SUPER>
inline bool isinstanceof(std::shared_ptr<SUPER> instance) requires(std::derived_from<SUB,SUPER>){
return isinstanceof<SUB>(*instance);
}
template<class SUPERANDSUB>
inline consteval bool isinstanceof(const SUPERANDSUB& x){
return true;
}
template<class SUPERANDSUB>
inline consteval bool isinstanceof(const SUPERANDSUB* x){
return true;
}
using dirpath = std::filesystem::path;
using byte = uint8_t;
using word = uint16_t;
using dword = uint32_t;
using qword = uint64_t;
using bytes = std::basic_string<byte>;
using byte_view = std::basic_string_view<byte>;
inline namespace literals{
inline constexpr const byte* operator ""_b(const char8_t* st,std::size_t){
return std::bit_cast<const byte*>(st);
}
inline constexpr byte operator ""_b(long long unsigned int x){
return static_cast<byte>(x);
}
inline constexpr bytes operator ""_bs(const char8_t* st,std::size_t l){
return {reinterpret_cast<const byte*>(st),l};
}
inline constexpr byte_view operator ""_bv(const char8_t* st,std::size_t l){
return {reinterpret_cast<const byte*>(st),l};
}
}
template<typename T> requires(std::is_integral_v<T>)
inline void data(byte* bv,T x,std::endian endianess=std::endian::little){
static_assert(std::endian::native==std::endian::little||std::endian::native==std::endian::big,u8"Unsupported mixed-endian system");
bytes _irep(sizeof(T),0_b);
if(endianess!=std::endian::native){
x = std::byteswap(x);
}
std::memcpy(bv,&x,sizeof(T));
}
template<typename T> requires(std::is_integral_v<T>)
inline bytes data(T x,std::endian endianess=std::endian::little){
bytes _irep(sizeof(T),0_b);
data<T>(_irep.data(),x,endianess);
return _irep;
}
template<typename T> requires(std::is_integral_v<T>)
inline T rdata(byte_view bv,std::endian endianess=std::endian::little){
T x;
std::memcpy(&x,bv.data(),sizeof(T));
if(endianess!=std::endian::native){
return std::byteswap(x);
}
return x;
}
template<typename Container>
class reverse_rangefor_loop{
Container& t;
public:
reverse_rangefor_loop(Container& cntr): t(cntr){}
auto begin(){
return t.rbegin();
}
auto cbegin() const{
return t.crbegin();
}
auto begin() const{
return cbegin();
}
auto end(){
return t.rend();
}
auto cend() const{
return t.crend();
}
auto end() const{
return cend();
}
auto rbegin(){
return t.begin();
}
auto crbegin() const{
return t.cbegin();
}
auto rbegin() const{
return crbegin();
}
auto rend(){
return t.end();
}
auto crend() const{
return t.cend();
}
auto rend() const{
return crend();
}
};
template<size_t bits>
class Flags{
std::bitset<bits> flg;
public:
constexpr Flags(size_t x) : flg(x){}
constexpr bool any() const{
return flg.any();
}
constexpr bool empty() const{
return !flg.any();
}
void clear(){
flg.reset();
}
constexpr bool operator&(const Flags& car) const{
return (flg&car.flg).any();
}
Flags& operator&=(const Flags& othr){
flg &= othr.flg;
return *this;
}
Flags& operator-=(const Flags& othr){
flg &= (~othr.flg);
return *this;
}
constexpr Flags operator&&(const Flags& othr) const{
return Flags(flg&othr.flg);
}
constexpr Flags& operator|=(const Flags& othr){
flg |= othr.flg;
return *this;
}
Flags operator|(const Flags& othr) const{
return {flg|othr.flg};
}
};
namespace logging{
const int
DEBUG=0,
INFO=1,
WARN=2,
ERR=3,
CRIT=4;
inline thread_local int loglvl = INFO;
using namespace std::literals;
inline std::unordered_map<int,std::u8string> llnames;
inline void set_loglvl(const int llv){
loglvl = llv;
}
class Logger{
std::u8string name;
public:
Logger(std::u8string_view name) : name(name){};
void log(std::u8string_view info,const int lvl=INFO){
if(llnames.empty()){
llnames.insert_or_assign(DEBUG,u8"debug"sv);
llnames.insert_or_assign(INFO,u8"info"sv);
llnames.insert_or_assign(WARN,u8"warn"sv);
llnames.insert_or_assign(ERR,u8"error"sv);
llnames.insert_or_assign(CRIT,u8"critical"sv);
}
if(lvl>=loglvl){
(lvl<ERR?fclog:fcerr) << name << u8'/' << llnames[lvl] << u8'/' << info << std::endl;
}
}
};
}
namespace _impl{
template<typename T>
concept has_get = requires(T x){
{x.get()};
};
template<typename T,typename U>
concept ok_eq = requires(T x, U y){
{x == y};
};
template<typename T>
struct TrHash : std::hash<T>{
using is_transparent = T;
using std::hash<T>::hash;
using std::hash<T>::operator();
template<typename U> requires(!std::same_as<T,U>)
constexpr size_t operator()(U&& x) const noexcept(noexcept(std::hash<std::remove_cvref_t<U>>()(std::forward<U>(x)))){
return std::hash<std::remove_cvref_t<U>>()(std::forward<U>(x));
}
};
template<typename T>
struct TrEq : std::equal_to<T>{
using is_transparent = T;
using std::equal_to<T>::equal_to;
using std::equal_to<T>::operator();//Prevent hiding
template<typename U> requires(!std::same_as<T,U>)
constexpr bool operator()(const T& x,U&& y) const noexcept(noexcept(x==std::forward<U>(y))){
return x==std::forward<U>(y);
}
//overload for smart pointer T
template<typename U> requires(!std::same_as<T,U>&&has_get<T>)
constexpr bool operator()(U&& x,const T& y) const noexcept(noexcept(std::forward<U>(x)==y.get())){
return std::forward<U>(x)==y.get();
}
template<typename U> requires(!std::same_as<T,U>&&ok_eq<U,T>)
constexpr bool operator()(U&& x,const T& y) const noexcept(noexcept(std::forward<U>(x)==y)){
return std::forward<U>(x)==y;
}
};
}
template<typename K,typename V>
using dfheq_umap = std::unordered_map<K,V,_impl::TrHash<K>,_impl::TrEq<K>>;
template<typename E>
using dfheq_uset = std::unordered_set<E,_impl::TrHash<E>,_impl::TrEq<E>>;
template<typename _K,typename _V>
class BiMap{
using K = std::remove_cvref_t<_K>;
using V = std::remove_cvref_t<_V>;
public:
using forward_type = dfheq_umap<K,const V*>;
using backward_type = dfheq_umap<V,const K*>;
private:
forward_type fwd;
backward_type bkwd;
template<typename ...Args>
BiMap andThen(const K& k,const V& v,Args&& ...a){
BiMap dup = *this;
dup.emplace(k,v);
return dup.andThen(std::forward<Args>(a)...);
}
BiMap andThen(){
return *this;
}
template<typename T,bool _const=false>
class iterator{
using iterator_type = std::conditional_t<_const,typename T::const_iterator,typename T::iterator>;
iterator_type it;
public:
using pointer = void;
using reference = std::pair<const K&,const V&>;
iterator() : it(){}
iterator(const iterator_type& it) : it(it){}
std::pair<const K&,const V&> operator*() const{
return {it->first,*(it->second)};
}
auto operator-(const iterator& other) const{
return it-other.it;
}
iterator& operator++(){
++it;
return *this;
}
iterator operator++(int){
return {it++};
}
iterator& operator--(){
--it;
return *this;
}
iterator operator--(int){
return {it--};
}
iterator& operator+=(std::make_signed_t<size_t> advance){
it += advance;
return *this;
}
iterator& operator-=(std::make_signed_t<size_t> advance){
it -= advance;
return *this;
}
bool operator==(const iterator<T,_const>& other) const{
return it==other.it;
}
};
public:
using forward_iterator = iterator<forward_type>;
using const_forward_iterator = iterator<forward_type,true>;
using backward_iterator = iterator<backward_type>;
using const_backward_iterator = iterator<backward_type,true>;
BiMap() = default;
/*Forward iterators*/
forward_iterator begin(){
return {fwd.begin()};
}
const_forward_iterator begin() const{
return {fwd.cbegin()};
}
const_forward_iterator cbegin() const{
return {fwd.cbegin()};
}
forward_iterator end(){
return {fwd.end()};
}
const_forward_iterator end() const{
return {fwd.cend()};
}
const_forward_iterator cend() const{
return {fwd.cend()};
}
/*Backward iterators*/
backward_iterator bbegin(){
return {bkwd.begin()};
}
const_backward_iterator bbegin() const{
return {bkwd.cbegin()};
}
const_backward_iterator cbbegin() const{
return {bkwd.cbegin()};
}
backward_iterator bend(){
return {bkwd.end()};
}
const_backward_iterator bend() const{
return {bkwd.cend()};
}
const_backward_iterator cbend() const{
return {bkwd.cend()};
}
static BiMap<K,V> of(const std::initializer_list<std::pair<K,V>>& a){
BiMap<K,V> e;
for(const auto& [k,v] : a){
e.emplace(k,v);
}
return e;
}
bool hasKey(const K& k) const{
return fwd.contains(k);
}
bool hasVal(const V& v) const{
return fwd.contains(v);
}
const V& lookup(const K& k) const{
return *fwd.at(k);
}
template<typename KK> requires(!std::same_as<std::remove_cvref_t<K>,std::remove_cvref_t<KK>>)
const V& lookup(KK&& matcher) const{
if(auto it = fwd.template find<KK>(std::forward<KK>(matcher));it!=fwd.cend()){
return *(it->second);
}
throw std::out_of_range("Bad BiMap::lookup: key not found");
}
const K& reversed_lookup(const V& v) const{
return *bkwd.at(v);
}
template<typename VV>requires(!std::same_as<std::remove_cvref_t<V>,std::remove_cvref_t<VV>>)
const K& reversed_lookup(VV&& matcher) const{
if(auto it = bkwd.template find<VV>(std::forward<VV>(matcher));it!=bkwd.cend()){
return *(it->second);
}
throw std::out_of_range("Bad BiMap::reversed_lookup: value not found");
}
void insert_or_assign(const K& k,const V& v){
const auto& fwpos = fwd.insert_or_assign(k,nullptr).first;
const K& fwkref = fwpos->first;
const auto& bkpos = bkwd.insert_or_assign(v,&fwkref).first;
const V& bkvref = bkpos->first;
fwd.at(k) = &bkvref;
}
//signature equivalent to std::unordered_map::try_emplace
template<typename ...Args>
bool emplace(const K& k,Args&& ...a){
if(fwd.contains(k))return false;
const auto& fwpos = fwd.insert_or_assign(k,nullptr).first;
const K& fwkref = fwpos->first;
const auto& bkpos = bkwd.emplace(
std::piecewise_construct,
std::forward_as_tuple(std::forward<Args>(a)...),
std::forward_as_tuple(&fwkref)).first;
const V& bkvref = bkpos->first;
fwd.at(k) = &bkvref;
return true;
}
};
template<typename T>
class fl_array{
T* block;
size_t _size;
void release(){
block = new T[_size];//not reassigned if allocation failed
}
public:
class resized : public u8_logic_error{
using u8_logic_error::u8_logic_error;
};
T* data(){
return block;
}
const T* data() const{
return block;
}
void reset(){
T* new_block = new T[_size];//exception if failed
delete[] block;
block = new_block;
}
void fill(const T& content){
for(size_t i=0uz;i<_size;++i){
block[i] = content;
}
}
using iterator = T*;
using const_iterator = const T*;
fl_array(size_t length) : block(new T[length]), _size(length){}
template<typename RAIter>
fl_array(RAIter&& bgn,RAIter&& end) : fl_array(end-bgn){
try{
iterator this_it = begin();
for(std::remove_reference_t<RAIter> it=bgn;this_it!=this->end();++this_it,++it){
*this_it = *it;
}
}catch(...){
delete[] block;
throw;
}
}
fl_array(size_t length,const T& content) : fl_array(length){
try{
fill(content);
}catch(...){
delete[] block;
throw;
}
}
fl_array(const fl_array& o) : block(new T[o._size]), _size(o._size){
(*this) = o;
}
fl_array(fl_array&& o) : block(o.block), _size(o._size){
o.release();//not released on allocation fail. Ctor exception will make the dtor not run, so we're not deleting the block.
}
fl_array& operator=(const fl_array& o){
if(this==&o){
return *this;
}
if(_size!=o._size){
throw resized(u8"Copy-assigning fl_array["s+cppp::to_u8string(o._size)+u8"] to fl_array["s+cppp::to_u8string(_size)+u8']');
}
for(size_t i=0uz;i<_size;++i){
(*this)[i] = o[i];
}
return *this;
}
fl_array& operator=(fl_array&& o){
if(this==&o){
return *this;
}
if(_size!=o._size){
throw resized(u8"Move-assigning fl_array["s+cppp::to_u8string(o._size)+u8"] to fl_array["s+cppp::to_u8string(_size)+u8']');
}
delete[] block;
block = o.block;
o.release();
return *this;
}
T& operator[](size_t ind){
return block[ind];
}
const T& operator[](size_t ind) const{
return block[ind];
}
size_t size() const{
return _size;
}
T& at(size_t ind){
if(ind<_size){
return block[ind];
}
throw std::out_of_range("fl_array access out of range: accessing index "s+std::to_string(ind)+" in a fl_array["s+std::to_string(_size)+']');
}
const T& at(size_t ind) const{
if(ind<_size){
return block[ind];
}
throw std::out_of_range("fl_array access out of range: accessing index "s+std::to_string(ind)+" in a fl_array["s+std::to_string(_size)+']');
}
iterator begin(){
return block;
}
const_iterator cbegin() const{
return block;
}
const_iterator begin() const{
return cbegin();
}
iterator end(){
return block+_size;
}
const_iterator cend() const{
return block+_size;
}
const_iterator end() const{
return cend();
}
~fl_array(){
delete[] block;
}
};
template<bool cond,typename T>
struct add_const_if{
using type = T;
};
template<typename T>
struct add_const_if<true,T>{
using type = const T;
};
template<bool B,typename T>
using add_const_if_t = add_const_if<B,T>::type;
template<typename T>
class rolling_array{
fl_array<T> arr;
size_t _begin;
size_t to_arr_ind(size_t ind) const{
return (_begin+ind)%arr.size();
}
template<bool>
class _iterator;
public:
using iterator = _iterator<false>;
using const_iterator = _iterator<true>;
rolling_array(size_t size) : arr(size), _begin(0uz){}
rolling_array(size_t size,const T& df_v) : arr(size,df_v), _begin(0uz){}
template<typename RAIter>
rolling_array(RAIter&& bgn,RAIter&& ens) : arr(bgn,ens), _begin(0uz){}
void push_back(const T& data){
arr[_begin++] = data;
_begin %= arr.size();
}
void push_back(T&& data){
arr[_begin++] = std::move(data);
_begin %= arr.size();
}
T& operator[](size_t ind){
return arr[to_arr_ind(ind)];
}
const T& operator[](size_t ind) const{
return arr[to_arr_ind(ind)];
}
iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
const_iterator begin() const;
const_iterator end() const;
size_t size() const{
return arr.size();
}
};
template<typename T>
template<bool const_>
class rolling_array<T>::_iterator{
using ra_t = add_const_if_t<const_,rolling_array<T>>;
ra_t& arr;
size_t ind;
public:
_iterator(ra_t& a,const size_t i) : arr(a), ind(i){}
bool operator==(const _iterator& other) const{
return (&arr==&other.arr)&&(ind==other.ind);
}
size_t operator-(const _iterator& other) const{
if(ind<other.ind){
return ind+(arr.size()-other.ind);
}
return other.ind-ind;
}
add_const_if_t<const_,T>& operator*() const{
return arr[ind];
}
_iterator& operator++(){
++ind;
return *this;
}
_iterator operator++(int){
_iterator dup{*this};
++(*this);
return dup;
}
_iterator& operator--(){
--ind;
return *this;
}
_iterator operator--(int){
_iterator dup{*this};
--(*this);
return dup;
}
_iterator& operator+=(size_t off){
ind += off;
return *this;
}
_iterator& operator-=(size_t off){
ind -= off;
return *this;
}
_iterator& operator+=(std::make_signed_t<size_t> off){
if(off>0ll){
ind += size_t(off);
}else{
ind -= size_t(-off);
}
return *this;
}
_iterator& operator-=(std::make_signed_t<size_t> off){
return (*this)+=(-off);
}
};
template<typename T>
rolling_array<T>::iterator rolling_array<T>::begin(){
return {*this,0uz};
}
template<typename T>
rolling_array<T>::iterator rolling_array<T>::end(){
return {*this,size()};
}
template<typename T>
rolling_array<T>::const_iterator rolling_array<T>::cbegin() const{
return {*this,0uz};
}
template<typename T>
rolling_array<T>::const_iterator rolling_array<T>::cend() const{
return {*this,size()};
}
template<typename T>
rolling_array<T>::const_iterator rolling_array<T>::begin() const{
return cbegin();
}
template<typename T>
rolling_array<T>::const_iterator rolling_array<T>::end() const{
return cend();
}
using codepoint = char32_t;
using codepoints = std::u32string;
namespace{
const constexpr byte utf8_byte_mask = 0b10000000u;
const constexpr byte utf8_body_bytes_mask = 0b00111111u;
}
inline void appendCodepointToString(codepoint cp,std::u8string& buf){
if(cp<=0x7fu){
buf += byte(cp);
}else if(cp<=0x7ffu){
buf += byte(0b11000000u)|byte(cp>>6u);
buf += utf8_byte_mask|byte(cp&utf8_body_bytes_mask);
}else if(cp<=0xffffu){
buf += byte(0b11100000u)|byte(cp>>12u);
buf += utf8_byte_mask|byte((cp>>6u)&utf8_body_bytes_mask);
buf += utf8_byte_mask|byte(cp&utf8_body_bytes_mask);
}else if(cp<=0x10ffffu){
buf += byte(0b11110000u)|byte(cp>>18u);
buf += utf8_byte_mask|byte(cp>>12u&utf8_body_bytes_mask);
buf += utf8_byte_mask|byte(cp>>6u&utf8_body_bytes_mask);
buf += utf8_byte_mask|byte(cp&utf8_body_bytes_mask);
}else{
throw u8_logic_error(u8"UTF-8 codepoint "sv+to_u8string(+cp)+u8" > U+10FFFF"sv);
}
}
inline std::u8string codepointToString(codepoint cp){
std::u8string buf;
appendCodepointToString(cp,buf);
return buf;
}
inline std::u8string codepoints_to_u8(const codepoints& cps){
std::u8string buf;
for(const codepoint& cp : cps){
appendCodepointToString(cp,buf);
}
return buf;
}
inline uint8_t countBytesForNextCodepoint(std::u8string_view sv){
constexpr static byte c = 0xff;
if(sv.empty()){
return 0u;
}
byte b0 = sv[0uz];
if(!((c^b0)&0b11110000u)){
return 4u;
}else if(!((c^b0)&0b11100000u)){
return 3u;
}else if(!((c^b0)&0b11000000u)){
return 2u;
}
return 1u;
}
inline codepoint next_codepoint(std::u8string_view& sv){
uint8_t count = countBytesForNextCodepoint(sv);
if(count==0u)return 0u;
byte b = sv.front();
sv = sv.substr(1uz);
if(count==1u){
return b;
}
codepoint buf = b&((1u<<(7u-count))-1u);
for(uint8_t i=0u;i<(count-1u);++i){
buf <<= 6u;
buf |= sv.front()&utf8_body_bytes_mask;
sv = sv.substr(1uz);
}
return buf;
}
inline codepoint next_codepoint(const std::u8string_view& sv){
//Note: not passing by value because it would create an ambiguous overload
//Copying a u8string_view is pretty cheap, though
std::u8string_view c{sv};
return next_codepoint(c);
}
inline codepoints codepoints_of(std::u8string_view sv){
codepoints cps;
while(!sv.empty()){
cps += next_codepoint(sv);
}
return cps;
}
template<typename C=char8_t>
inline std::vector<std::basic_string<C>> split(const std::basic_string_view<C>& s,const C sep,bool keep_empty=false){
std::vector<std::u8string> tkns;
tkns.emplace_back();
for(const char8_t& ch : s){
if(ch==sep){
if(keep_empty||!tkns.back().empty()){
tkns.emplace_back();
}
}else{
tkns.back().push_back(ch);
}
}
if(!keep_empty&&tkns.back().empty()){
tkns.pop_back();
}
return tkns;
}
template<typename C=char8_t>
inline std::vector<std::basic_string<C>> split(const std::basic_string<C>& s,const C sep,bool keep_empty=false){
return split<C>(std::basic_string_view<C>(s),sep,keep_empty);
}
template<typename T>
inline T sgetnum(std::u8string_view sv){
if(sv.empty()){
throw std::invalid_argument("sgetnum: empty input!");
}
bool negate;
if((negate = sv.front()==u8'-')){
if constexpr(std::is_unsigned_v<T>){
throw std::invalid_argument("sgetnum: must be unsigned!");
}
sv = sv.substr(1uz);
}
constexpr static T TEN{static_cast<T>(10)};
constexpr static T MAXDTEN{std::numeric_limits<T>::max()/10};
T x{static_cast<T>(0)};
for(const char8_t& c : sv){
if(x>MAXDTEN){
throw std::overflow_error("sgetnum: overflow!");
}
x *= TEN;
if(c<u8'0'||c>u8'9'){
throw std::invalid_argument("sgetnum: non-digit found!");
}
x += static_cast<T>(c-u8'0');
}
return x;
}
template<typename Ct=char8_t,function_type<bool,const std::basic_string<Ct>&,size_t> tokFun>
inline size_t tokenize(const std::basic_string_view<Ct>& st,const Ct sep,tokFun fn,const bool filter_adjacent_seps=true,bool dont_sep_in_quotes=false){
std::basic_string<Ct> tkn;
bool str=false;
size_t d=0;
for(const Ct& ch : st){
++d;
if(dont_sep_in_quotes&&(ch==Ct('\"'))){
str = !str;
}else if((ch==sep)&&(!str)){
if(!(tkn.empty()&&filter_adjacent_seps)){
if(fn(tkn,d-1)){
return d;
}
}
tkn.clear();
}else{
tkn += ch;
}
}
if(!tkn.empty()){
fn(tkn,d-1);
}
return st.size();
}
template<typename T>
struct show_diagnostic{};
template<typename T>
inline std::u8string str(const T& t){
if constexpr(std::is_base_of_v<std::u8string,T>){
return t;
}else if constexpr(supports_std_tostring<T>){
return to_u8string(t);
}else if constexpr(std::is_base_of_v<bytes,T>){
std::u8string dump;
for(const byte& b : t){
dump += to_u8string(b)+u8' ';//decimal dump
}
return dump.substr(0,dump.size()-1);
}else if constexpr(std::is_base_of_v<codepoints,T>){
std::u8string dump;
for(const codepoint& b : t){
dump += u8"U+u8"s+to_u8string(b)+u8"sdec "s;//decimal dump
}
return dump.substr(0uz,dump.size()-1uz);
}else{
show_diagnostic<T>::error();
//"Un-repr-able"
std::unreachable();
}
}
template<typename T>
inline std::u8string repr(const T& t){
if constexpr(std::is_base_of_v<std::u8string,T>){
return std::quoted(t);
}else{
return str<T>(t);
}
}
class file_error : public u8_runtime_error{
using u8_runtime_error::u8_runtime_error;
};
class file_broken : public file_error{
using file_error::file_error;
};
class bad_fileop : public file_error{
using file_error::file_error;
};
class eof_error : public file_error{
public:
eof_error() : file_error(u8"EOF while reading file"sv){}
};
#ifdef __GNUC__
using streamed_byte = char;
#else
using streamed_byte = uint8_t;
#endif
class BinFile{
std::basic_fstream<streamed_byte> handle;
[[noreturn]] void _broken() const{
throw file_broken(u8"File I/O failed: External error"sv);
}
void optexc(){
auto state = handle.rdstate();
if(state&handle.badbit){
_broken();
}else if(state&handle.failbit){
throw bad_fileop(u8"File I/O failed: Logic error"sv);
}else if(!handle.good()){
throw file_broken(u8"File I/O failed: Unknown error"sv);
}
handle.clear();
}
bool had_eof() const{
return handle.eof();
}
public:
BinFile(dirpath pat,std::ios_base::openmode flags) : handle(pat,std::ios_base::binary|flags){
if(!handle.good()){
throw file_broken(u8"File open failed"sv);
}
}
size_t readsomeinto(size_t nchars,byte* buf,const bool allow_eof=true){
size_t nread = handle.readsome(reinterpret_cast<streamed_byte*>(buf),nchars);
if(had_eof()){
if(handle.bad()){
_broken();//badbit is NOT set when EOF, so something else bad happened
}
if(allow_eof){
handle.clear();//remove the fail and eof bits.
}else{
throw eof_error();
}
}
optexc();
return nread;
}
//If an exception is raised(e.g. due to not enough data), buf is NOT left untouched.
void readinto(size_t nchars,byte* buf){
handle.read(reinterpret_cast<streamed_byte*>(buf),nchars);
if(had_eof()){
throw eof_error();
}
optexc();
if(static_cast<size_t>(handle.gcount())!=nchars){
throw file_broken(u8"readinto: Not enough data"sv);
}
}
bytes readsome(size_t nchars){
fl_array<byte> buf{nchars};
bytes x(readsomeinto(nchars,buf.data()),0_b);
std::memcpy(x.data(),buf.data(),x.size());
return x;
}
bytes read(size_t nchars){
bytes x(nchars,0_b);
readinto(nchars,x.data());
return x;
}
byte readbyte(){
return read(sizeof(byte)).front();
}
word readword(std::endian endianess=std::endian::little){
return rdata<word>(read(sizeof(word)),endianess);
}
dword readdword(std::endian endianess=std::endian::little){
return rdata<dword>(read(sizeof(dword)),endianess);
}
qword readqword(std::endian endianess=std::endian::little){
return rdata<qword>(read(sizeof(qword)),endianess);
}
void write(const byte* bytes,const size_t n){
handle.write(reinterpret_cast<const streamed_byte*>(bytes),n);
optexc();
}
void write(const byte_view& bytes){
write(bytes.data(),bytes.size());
}
void write(byte b){
write(&b,1uz);
}
void write_word(word w,std::endian endianess=std::endian::little){
write(data(w,endianess));
}
void write_dword(dword dw,std::endian endianess=std::endian::little){
write(data(dw,endianess));
}
void write_qword(qword qw,std::endian endianess=std::endian::little){
write(data(qw,endianess));
}
std::streampos tell(){
return handle.tellg();//fstream keeps one pointer for R/W
}
void seek(const std::streampos& where){
handle.seekg(where);//ditto
}
};
namespace _impl{
template<typename T>
concept has_reserve = requires(T t){
{t.reserve(std::declval<std::size_t>)};
};
template<typename T,typename ...A>
concept has_emplaceback = requires(T t){
{t.emplace_back(std::declval<A>()...)};
};
template<typename T,typename ...A>
concept has_pushback = requires(T t){
{t.push_back(std::declval<A>()...)};
};
template<typename T,typename ...A>
concept has_emplace = requires(T t){
{t.emplace(std::declval<A>()...)};
};
template<typename T,typename ...A>
concept has_insert = requires(T t){
{t.insert(std::declval<A>()...)};
};
template<ra_seq_container C>
void _expand_pack(C&){}
template<ra_seq_container C,typename I,typename ...A>
void _expand_pack(C& cont,I&& first,A&& ...other){
if constexpr(has_emplaceback<C,I>){
cont.emplace_back(std::forward<I>(first));
}else if constexpr(has_pushback<C,I>){
cont.push_back(std::forward<I>(first));
}else if constexpr(has_emplace<C,I>){
cont.emplace(std::forward<I>(first));
}else if constexpr(has_insert<C,I>){
cont.insert(std::forward<I>(first));
}else{
static_assert(false,"Unsupported container type");
}
_expand_pack(cont,std::forward<A>(other)...);
}
}
template<ra_seq_container C,typename ...A>
void expand(C& cont,A&& ...a){
if constexpr(_impl::has_reserve<C>){
cont.reserve(sizeof...(a));
}
_impl::_expand_pack<C,A...>(cont,std::forward<A>(a)...);
}
template<typename T> requires(!std::is_reference_v<T>)
class Lazy{
class CopyWrapper{
T x;
public:
constexpr T& d() noexcept{
return x;
}
constexpr const T& d() const noexcept{
return x;
}
constexpr CopyWrapper() noexcept(std::is_nothrow_default_constructible_v<T>) : x(){}
constexpr CopyWrapper(const T& dup) noexcept(std::is_nothrow_copy_constructible_v<T>): x(dup){}
constexpr CopyWrapper(std::remove_const_t<T>&& mov)
noexcept(noexcept(T(std::move(mov)))) : x(std::move(mov)){}
constexpr CopyWrapper(const CopyWrapper& dup) noexcept(std::is_nothrow_copy_constructible_v<T>) : CopyWrapper(dup.x){}
constexpr CopyWrapper(CopyWrapper&& mov) noexcept(std::is_nothrow_move_constructible_v<T>) : CopyWrapper(std::move(mov.x)){}
template<typename ...A>
constexpr CopyWrapper(A&& ...a)
noexcept(noexcept(T(std::forward<A>(a)...))) : x(std::forward<A>(a)...){}
CopyWrapper& operator=(const CopyWrapper&){
throw cppp::u8_logic_error("Lazy<T> set twice");
}
CopyWrapper& operator=(CopyWrapper&&){
throw cppp::u8_logic_error("Lazy<T> set twice");
}
};
std::optional<CopyWrapper> data;
public:
constexpr Lazy() noexcept : data(std::nullopt){}
template<typename ...A>
void emplace(A&& ...a) noexcept(noexcept(data.emplace(std::forward<A>(a)...))){
data.emplace(std::forward<A>(a)...);
}
void operator=(const T& d) noexcept(noexcept(data = CopyWrapper(d))){
data = CopyWrapper(d);
}
void operator=(std::remove_const_t<T>&& d) noexcept(noexcept(data = CopyWrapper(std::move(d)))){
data = CopyWrapper(std::move(d));
}
constexpr bool has_v() const noexcept{
return data.has_value();
}
T& checked_access(){
return data.value().d();
}
const T& checked_access() const{
return data.value().d();
}
constexpr T& operator*() noexcept{
return data->d();
}
constexpr const T& operator*() const noexcept{
return data->d();
}
constexpr T* operator->() noexcept{
return std::addressof(**this);
}
constexpr const T* operator->() const noexcept{
return std::addressof(**this);
}
};
template<typename T>
class better_shared_ptr{
std::shared_ptr<T> _p;
public:
using element_type = T;
better_shared_ptr(std::shared_ptr<T>&& s) : _p(std::move(s)){}
better_shared_ptr() noexcept : _p(){}
better_shared_ptr(std::nullptr_t) noexcept : _p(nullptr){}
better_shared_ptr(T* const && ptr) : _p(ptr){}
template<std::derived_from<T> U>
better_shared_ptr(U* const && ptr) : _p(ptr){}
explicit better_shared_ptr(const T*& ptr) : _p(ptr){}
T* operator->() const noexcept{
return _p.get();
}
T& operator*() const noexcept{
return *_p;
}
explicit operator bool() const noexcept{
return static_cast<bool>(_p);
}
T& operator[](std::ptrdiff_t d) const requires(std::is_unbounded_array_v<T>){
return _p[d];
}
T* get() const noexcept{
return _p.get();
}
void reset(T* ptr){
_p.reset(ptr);
}
bool operator==(const better_shared_ptr<T>& other) const noexcept{
return _p==other._p;
}
bool operator==(std::nullptr_t) const noexcept{
return !_p;
}
template<typename E>
better_shared_ptr<E> dcast(){
return std::dynamic_pointer_cast<E,T>(_p);
}
void swap(better_shared_ptr<T>& other){
using std::swap;
swap(_p,other._p);
}
};
template<typename T>
class bsp_pbcv{
better_shared_ptr<T>& e;
public:
bsp_pbcv(better_shared_ptr<T>& e) : e(e){}
operator better_shared_ptr<T>&() const{
return e;
}
better_shared_ptr<T>& operator()() const{
return e;
}
};
template<typename T>
class enable_better_shared_from_this : public std::enable_shared_from_this<T>{
protected:
better_shared_ptr<T> better_shared_from_this(){
return {static_cast<std::enable_shared_from_this<T>*>(this)->shared_from_this()};
}
better_shared_ptr<const T> better_shared_from_this() const{
return {static_cast<const std::enable_shared_from_this<T>*>(this)->shared_from_this()};
}
};
template<typename T>
void swap(better_shared_ptr<T>& p,better_shared_ptr<T>& q){
p.swap(q);
}
}
namespace std{
template<typename T>
struct hash<::cppp::better_shared_ptr<T>>{
size_t operator()(const ::cppp::better_shared_ptr<T>& p) const noexcept{
return std::hash<typename std::shared_ptr<T>::element_type*>()(p.get());
}
};
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment