Last active
September 18, 2023 13:33
-
-
Save 00001H/e5969595791d6ff060d91a95bbe59f2b to your computer and use it in GitHub Desktop.
A lightweight C++ enhancement library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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