David Kastrup <[email protected]> writes: > Dan Eble <[email protected]> writes: > >> On May 5, 2020, at 13:37, David Kastrup <[email protected]> wrote: >>> >>> What I have ready-to-use is something that stores like an SCM value but >>> behaves like a Smob pointer with regard to -> and * usage. >> >> Oh. I believe I have some of that too. Excerpt: >> >> // specialization for pointers >> template <class T> >> class ly_scm_t<T *> >> { >> private: >> using traits = ly_scm_traits<T *>; >> >> private: >> SCM scm_ = SCM_UNDEFINED; >> >> public: >> ly_scm_t () = default; >> ly_scm_t (const ly_scm_t &) = default; >> ly_scm_t &operator = (const ly_scm_t &) = default; >> ~ly_scm_t () = default; >> >> explicit ly_scm_t (T *v) : scm_ (traits::to_scm (v)) {} >> >> explicit operator bool () const { return traits::has_value (scm_); } >> operator T *() const { return traits::to_value (scm_); } >> T *operator ->() { return operator T * (); } >> // TODO: operator * >> }; > > Well, mine is a lot less formal I guess. Something akin to > > template <class T> > class Scm_ptr > { > SCM scm_; > public: > Scm_ptr () : scm_ (SCM_UNDEFINED) {} > Scm_ptr (SCM v) : scm_ (v) > { > assert (SCM_UNBNDP (v) || T::is_smob (v)); > } > Scm_ptr (const T *p) : scm_ (p ? p->self_scm () : SCM_UNDEFINED) {} > Scm_ptr & operator = (T* p) > { > scm_ = p ? p->self_scm () : SCM_UNDEFINED; > return *this; > } > Scm_ptr & operator = (SCM s) > { > if (SCM_UNBNDP (s) || LY_ASSERT_SMOB (T, s, 1)) > scm_ = s; > else > scm_ = SCM_UNDEFINED; > return *this; > } > T * operator -> () const > { > return dynamic_cast<T *> (T::unchecked_unsmob (scm_)); > } > operator T * () const > { > return dynamic_cast<T *> (T::unchecked_unsmob (scm_)); > } > operator SCM () const > { > return scm_; > } > operator bool () const > { > return SCM_BNDP (scm_); > } > }; > > One thing here is that being under control of a wrapper, one can use > unchecked unsmobs. I am not sure about conversion to SCM: this uses a > conversion operator. I was tempted to use operator & instead. I did > not have any traits in mind yet, but formalising the allocation/garbage > protection thing would certainly call for something identifiable with > regard to types that LilyPond knows how to allocate/protect.
To expound somewhat: a vector class (replete with iterators etc) implemented through SCM would internally consist of one SCM cell holding a Scheme vector and one size_t number for its current length. The capacity would be the actual size of the Scheme vector (with extra slots filled with SCM_UNBNDP). So the garbage collector footprint is basically "pouo" (look up Scheme struct vtables for the meaning). A Smob allocator basically collects the strings of its constituents at their respective offsets when it is first called and initializes a Scheme vtable with it, and then uses this vtable to allocate the smob as an SCM value. Simple_smob can create a "smobbed copy" on demand in a similar way. If everything can be represented/mapped in similar manner, the Scheme garbage collector does not need any interaction with user-written code for doing its job. And I really think that's where we want to go in order to get memory semantics and behavior that are "Guile orthodox" to a degree that we'll get more attention for problems we are experiencing than what we have been working with so far. -- David Kastrup
