diff --git a/Kernel/Abstractions/blackbox.hpp b/Kernel/Abstractions/blackbox.hpp index ebe05c110..1f9dc799c 100644 --- a/Kernel/Abstractions/blackbox.hpp +++ b/Kernel/Abstractions/blackbox.hpp @@ -9,11 +9,12 @@ #ifndef BLACKBOX_H #define BLACKBOX_H #include "basic.hpp" +#include "sharedptr.hpp" /** * @brief A template class representing an opaque pointer. */ -class blackbox_rep : public abstract_struct { +class blackbox_rep { public: inline blackbox_rep () {} inline virtual ~blackbox_rep () {} @@ -22,11 +23,10 @@ class blackbox_rep : public abstract_struct { virtual tm_ostream& display (tm_ostream& out)= 0; }; -class blackbox { -public: - ABSTRACT_NULL (blackbox); +class blackbox : public counted_ptr { + using base::counted_ptr; + template friend blackbox close_box (const T&); }; -ABSTRACT_NULL_CODE (blackbox); template class whitebox_rep : public blackbox_rep { public: @@ -121,7 +121,7 @@ type_box (blackbox bb) { template blackbox close_box (const T& data) { - return tm_new> (data); + return blackbox (blackbox::make> (data)); } /** diff --git a/Kernel/Abstractions/sharedptr.hpp b/Kernel/Abstractions/sharedptr.hpp new file mode 100644 index 000000000..c1e96bc94 --- /dev/null +++ b/Kernel/Abstractions/sharedptr.hpp @@ -0,0 +1,198 @@ +/** \file sharedptr.hpp + * \copyright GPLv3 + * \details Defines smart pointers with reference counting. No spin lock is + * applied. + * \author jingkaimori + * \date 2024 + */ + +#pragma once + +#include "fast_alloc.hpp" +#include + +struct ref_counter_base { + virtual void inc_count ()= 0; + virtual void dec_count ()= 0; + virtual void* get () = 0; +}; + +/** + * @brief Structure representing an object with a reference count. + * @tparam T Actual type of inner object, must be a complete type + */ +template struct ref_counter : ref_counter_base { + /// @brief the reference count of the object + int ref_count; + T content; + + /** + * @brief in-place construct an object, and set its reference count to 1. + * @tparam Params types of parameters required by constructor of object + */ + template + explicit ref_counter (Params&&... p) + : ref_count (1), content (std::forward (p)...){}; + + void inc_count () { ref_count++; } + /** + * @brief decrement the reference count of the object and delete it if the + * count reaches 0. + */ + void dec_count () { + ref_count--; + if (ref_count == 0) { + tm_delete (this); + } + } + void* get () { return &content; } +}; + +/** + * @brief Smart pointer with reference counting. + * @details + * To use smart pointer, counted_ptr class should be inherited publicly, and the + * type exploited to user of the smart pointer should be provided. If null + * pointer may be held inside the smart pointer, parameter nullable should be + * set to true explictly. + * + * The class derived from pointer should provide proper constructor so that user + * can instantiate pointer from arguments of other type. Operator should be + * overloaded at the derived class, rather than the type of underlying object. + * @tparam T The type which user of pointer get from indirection of the smart + * pointer. + */ +template class counted_ptr { + +protected: + using counter_t= ref_counter_base; + /** + * @brief short-hand name of the class, avoiding duplicated type name of + * underlying object inside template. If the derived class is also template + * class, this declaration should be imported explictly with `using` clause. + */ + using base= counted_ptr; + explicit counted_ptr (counter_t* c) + : counter (c), rep (static_cast (c->get ())) {} + /** + * @tparam Stored the real type of underlying object, may differ from types + * exploits to user. This type must be complete type. + */ + template + static counter_t* make (Params&&... p) { + return tm_new> (std::forward (p)...); + } + +private: + /** + * @brief Opaque pointer to the counter that holds the instance of the + * underlying object + */ + counter_t* counter; + +public: + /** + * cached pointer to the underlying object + */ + T* rep; + + counted_ptr () : counter (nullptr), rep (nullptr) { + static_assert (nullable, + "null pointer is not allowed in non-null smart pointer."); + } + /** + * copy constructor of the smart pointer + */ + counted_ptr (const counted_ptr& x) + : counter (x.counter), rep (x.rep) { + if constexpr (nullable) { + if (counter != nullptr) { + counter->inc_count (); + } + } + else { + counter->inc_count (); + } + } + ~counted_ptr () { + if constexpr (nullable) { + if (counter != nullptr) { + counter->dec_count (); + } + } + else { + counter->dec_count (); + } + } + /** + * @brief Decrement the reference count for the old object (`*this`) and + * increments the reference count for the new object. + * @param x the new object + */ + counted_ptr& operator= (counted_ptr& x) { + if constexpr (nullable) { + if (x.counter != nullptr) { + x.counter->inc_count (); + } + if (this->counter != nullptr) { + this->counter->dec_count (); + } + } + else { + x.counter->inc_count (); + this->counter->dec_count (); + } + this->counter= x.counter; + this->rep = x.rep; + return *this; + } + /** + * @brief Steal underlying reference counter from rvalue temprary object, then + * leave the old counter (`this->counter`) in the temprary object. Finally the + * temprary object will be destroyed along with old object held by `*this` + * @param x the new object + */ + counted_ptr& operator= (counted_ptr&& x) { + std::swap (this->counter, x.counter); + std::swap (this->rep, x.rep); + return *this; + } + /** + * @brief pointer dereference operator + */ + T* operator->() { + if constexpr (nullable) { + if (counter != nullptr) { + return rep; + } + else { + return nullptr; + } + } + else { + return rep; + } + } + /** + * @brief pointer dereference operator, call from const pointer to get const + * object + */ + const T* operator->() const { return operator->(); } + bool is_nil () const { + if constexpr (nullable) { + return counter == nullptr; + } + else { + return false; + } + } +}; + +/** + * @brief adapter of legacy is_nil function. + */ +template +inline bool +is_nil (const counted_ptr x) { + return x.is_nil (); +} diff --git a/Kernel/Containers/hashtree.hpp b/Kernel/Containers/hashtree.hpp index 89d949488..9db94b7ef 100644 --- a/Kernel/Containers/hashtree.hpp +++ b/Kernel/Containers/hashtree.hpp @@ -15,12 +15,13 @@ #define HASHTREE_H #include "hashmap.hpp" +#include "sharedptr.hpp" template class hashtree; template int N (hashtree tree); template bool is_nil (hashtree tree); -template class hashtree_rep : concrete_struct { +template class hashtree_rep { hashmap> children; public: @@ -75,38 +76,27 @@ template class hashtree_rep : concrete_struct { * of NULL pointers so to speak). These NULL nodes are created by passing * a boolean value to the hashtree constructor. One cannot accidentally * obtain a NULL element by e.g. accessing a child (see below). - * - * In general, I tried to imitate the TeXmacs-way of memory management - * as closely as possibly, however the workaround is not that pretty. - * As more elegant way might be to modify the hashmap class so that - * a hashmap contains only a pointer to a function that returns - * a default value instead of a instance of a value-element. - * But I didn't want to modify core TeXmacs code. ******************************************************************************/ -template class hashtree { - // CONCRETE_TEMPLATE_2(hashtree,K,V); - hashtree_rep* rep; +template +class hashtree : public counted_ptr, true> { + using base= typename counted_ptr, true>::base; // this constructor always returns a NULL element - inline hashtree (bool) : rep (NULL) {} + inline hashtree (bool) : base () {} // ensures that this hashtree has a rep void realize (); public: - inline hashtree (const hashtree&); - inline ~hashtree (); - inline hashtree& operator= (hashtree x); - // default constructor returns a non-NULL node, which does not have a value - inline hashtree () : rep (tm_new> ()) {} + inline hashtree () : base (base::make ()) {} // returns a non-NULL node, that has value - inline hashtree (V val) : rep (tm_new> (val)) {} + inline hashtree (V val) : base (base::make (val)) {} // returns this node's value - inline hashtree_rep* operator->(void); + inline hashtree_rep* operator->(); // returns this node's child with the label "key". If the node doesn't // have such a child, an error is raised. @@ -120,8 +110,7 @@ template class hashtree { inline hashtree operator[] (K key); // rw access friend class hashtree_rep; - friend bool is_nil (hashtree ht); - friend int N (hashtree ht); + friend int N (hashtree ht); }; #include "hashtree.ipp" diff --git a/Kernel/Containers/hashtree.ipp b/Kernel/Containers/hashtree.ipp index 20ed77a30..d37bba9e2 100644 --- a/Kernel/Containers/hashtree.ipp +++ b/Kernel/Containers/hashtree.ipp @@ -15,33 +15,6 @@ #define HASHTREE_C #include "hashtree.hpp" -/****************************************************************************** - * Methods normally provided by - * CONCRETE_TEMPLATE_2_CODE(hashtree,class,K,class,V); - ******************************************************************************/ - -template -inline hashtree::hashtree (const hashtree& x) : rep (x.rep) { - if (this->rep != NULL) INC_COUNT (this->rep); -} - -template inline hashtree::~hashtree () { - if (this->rep != NULL) DEC_COUNT (this->rep); -} - -template -inline hashtree& -hashtree::operator= (hashtree x) { - if (this->rep != NULL) DEC_COUNT (this->rep); - this->rep= x.rep; - if (x.rep != NULL) INC_COUNT (x.rep); - return *this; -} - -/****************************************************************************** - * Methods of hashtree_rep - ******************************************************************************/ - template inline bool hashtree_rep::contains (K key) { @@ -81,9 +54,8 @@ hashtree_rep::get_label () { template inline void hashtree::realize () { - if (rep == NULL) { - rep= tm_new> (); - INC_COUNT (rep); + if (this->is_nil ()) { + *this= hashtree (); } } @@ -96,7 +68,7 @@ inline hashtree_rep* hashtree::operator->(void) { // always make sure there is a rep! realize (); - return rep; + return this->rep; } template @@ -121,7 +93,7 @@ hashtree::operator() (K key) { template inline bool is_nil (hashtree ht) { - return ht.rep == NULL; + return ht.is_nil (); } template diff --git a/System/IO/tm_ostream.cpp b/System/IO/tm_ostream.cpp index ba6a4826b..988a36865 100644 --- a/System/IO/tm_ostream.cpp +++ b/System/IO/tm_ostream.cpp @@ -21,7 +21,7 @@ * Routines for abstract base class ******************************************************************************/ -tm_ostream_rep::tm_ostream_rep () : ref_count (0) {} +tm_ostream_rep::tm_ostream_rep () {} tm_ostream_rep::~tm_ostream_rep () {} void tm_ostream_rep::flush () {} @@ -116,21 +116,21 @@ std_ostream_rep::flush () { class buffered_ostream_rep : public tm_ostream_rep { public: - tm_ostream_rep* master; - string buf; + tm_ostream master; + string buf; public: - buffered_ostream_rep (tm_ostream_rep* master); + buffered_ostream_rep (tm_ostream master); ~buffered_ostream_rep (); bool is_writable () const; void write (const char*); }; -buffered_ostream_rep::buffered_ostream_rep (tm_ostream_rep* master2) +buffered_ostream_rep::buffered_ostream_rep (tm_ostream master2) : master (master2) {} -buffered_ostream_rep::~buffered_ostream_rep () { DEC_COUNT (master); } +buffered_ostream_rep::~buffered_ostream_rep () {} bool buffered_ostream_rep::is_writable () const { @@ -146,33 +146,9 @@ buffered_ostream_rep::write (const char* s) { * Abstract user interface ******************************************************************************/ -tm_ostream::tm_ostream () : rep (tm_new ()) { - INC_COUNT (this->rep); -} -tm_ostream::tm_ostream (char* s) : rep (tm_new (s)) { - INC_COUNT (this->rep); -} -tm_ostream::tm_ostream (FILE* f) : rep (tm_new (f)) { - INC_COUNT (this->rep); -} -tm_ostream::tm_ostream (const tm_ostream& x) : rep (x.rep) { - INC_COUNT (this->rep); -} -tm_ostream::tm_ostream (tm_ostream_rep* rep2) : rep (rep2) { - INC_COUNT (this->rep); -} -tm_ostream::~tm_ostream () { DEC_COUNT (this->rep); } -tm_ostream_rep* -tm_ostream::operator->() { - return rep; -} -tm_ostream& -tm_ostream::operator= (tm_ostream x) { - INC_COUNT (x.rep); - DEC_COUNT (this->rep); - this->rep= x.rep; - return *this; -} +tm_ostream::tm_ostream () : base (make ()) {} +tm_ostream::tm_ostream (char* s) : base (make (s)) {} +tm_ostream::tm_ostream (FILE* f) : base (make (f)) {} bool tm_ostream::operator== (tm_ostream& out) { return (&out == this); @@ -190,25 +166,20 @@ tm_ostream::flush () { void tm_ostream::buffer () { - rep= tm_new (rep); - INC_COUNT (rep); + *this= tm_ostream (make (*this)); } string tm_ostream::unbuffer () { buffered_ostream_rep* ptr= (buffered_ostream_rep*) rep; - rep = ptr->master; - string r = ptr->buf; - INC_COUNT (rep); - DEC_COUNT (ptr); + string r = ptr->buf; + *this = ptr->master; return r; } void tm_ostream::redirect (tm_ostream x) { - INC_COUNT (x.rep); - DEC_COUNT (this->rep); - this->rep= x.rep; + *this= x; } /****************************************************************************** diff --git a/System/IO/tm_ostream.hpp b/System/IO/tm_ostream.hpp index 7a83b5b3d..2beb43ec7 100644 --- a/System/IO/tm_ostream.hpp +++ b/System/IO/tm_ostream.hpp @@ -13,14 +13,12 @@ #define OUT_STREAM_HPP // #include "url.hpp" +#include "sharedptr.hpp" #include "string.hpp" #include class tm_ostream; class tm_ostream_rep { -public: - int ref_count; - public: tm_ostream_rep (); virtual ~tm_ostream_rep (); @@ -33,9 +31,8 @@ class tm_ostream_rep { friend class tm_ostream; }; -class tm_ostream { -public: - tm_ostream_rep* rep; +class tm_ostream : public counted_ptr { + using base::counted_ptr; public: static tm_ostream private_cout; @@ -47,12 +44,7 @@ class tm_ostream { tm_ostream (); tm_ostream (char*); tm_ostream (FILE*); - tm_ostream (const tm_ostream&); - tm_ostream (tm_ostream_rep*); - ~tm_ostream (); - tm_ostream_rep* operator->(); - tm_ostream& operator= (tm_ostream x); - bool operator== (tm_ostream&); + bool operator== (tm_ostream&); void clear (); void flush (); diff --git a/lolly/data/string_u16.cpp b/lolly/data/string_u16.cpp index 2355e48b4..7c4109c55 100644 --- a/lolly/data/string_u16.cpp +++ b/lolly/data/string_u16.cpp @@ -81,19 +81,18 @@ string_u16_rep::reserve (int new_n) { } } -string_u16::string_u16 (char16_t c) : rep (tm_new (1)) { - rep->a[0]= c; -}; +string_u16::string_u16 (char16_t c) : base (make (1)) { rep->a[0]= c; }; -string_u16::string_u16 (const string_u16_view& c) - : rep (tm_new (c.N)) { +string_u16::string_u16 (const string_u16_view& c) : base (make (c.N)) { + char16_t* a= rep->a; for (int i= 0; i < c.N; i++) - rep->a[i]= c.a[i]; + a[i]= c.a[i]; }; -string_u16::string_u16 (char16_t c, int n) : rep (tm_new (n)) { +string_u16::string_u16 (char16_t c, int n) : base (make (n)) { + char16_t* a= rep->a; for (int i= 0; i < n; i++) - rep->a[i]= c; + a[i]= c; }; string_u16 diff --git a/lolly/data/string_u16.hpp b/lolly/data/string_u16.hpp index 0dcc8fe7b..72678257d 100644 --- a/lolly/data/string_u16.hpp +++ b/lolly/data/string_u16.hpp @@ -9,8 +9,8 @@ #pragma once -#include "classdef.hpp" #include "fast_alloc.hpp" +#include "sharedptr.hpp" #include "string_view.hpp" namespace lolly { @@ -20,7 +20,7 @@ using string_u16_view= lolly::data::string_view; class string_u16; -class string_u16_rep : concrete_struct { +class string_u16_rep { int n; int a_N; char16_t* a; @@ -58,17 +58,18 @@ class string_u16_rep : concrete_struct { friend int N (string_u16 a); }; -class string_u16 { - CONCRETE (string_u16); +class string_u16 : public counted_ptr { - inline string_u16 () : rep (tm_new ()) {} - inline explicit string_u16 (int n) : rep (tm_new (n)) {} +public: + inline string_u16 () : base (make ()) {} + inline explicit string_u16 (int n) : base (make (n)) {} template - string_u16 (const char16_t (&s)[N_]) : rep (tm_new (N_ - 1)) { + string_u16 (const char16_t (&s)[N_]) : base (make (N_ - 1)) { constexpr int n= N_ - 1; + char16_t* a= rep->a; for (int i= 0; i < n; i++) - rep->a[i]= s[i]; + a[i]= s[i]; }; string_u16 (char16_t c); @@ -92,7 +93,6 @@ class string_u16 { inline char16_t& operator[] (int i) { return rep->a[i]; } }; -CONCRETE_CODE (string_u16); inline int N (string_u16 a) {