X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Finclude%2Fsmobs.hh;h=00217866dc07dc92eff400e23050d50b46f67a3b;hb=6786ba7b5cd73f94eec0a49fd68d0e6d9d283437;hp=3ee2ef80abce09b44b2acadf1081173eb6579ce6;hpb=2f7cc8c9e89474feb89d1c435d8dc9b43235cc1c;p=lilypond.git diff --git a/lily/include/smobs.hh b/lily/include/smobs.hh index 3ee2ef80ab..00217866dc 100644 --- a/lily/include/smobs.hh +++ b/lily/include/smobs.hh @@ -21,6 +21,7 @@ #define SMOBS_HH #include "lily-guile.hh" +#include "lily-proto.hh" #include "warn.hh" #include @@ -94,14 +95,36 @@ Complex smobs are created by deriving from Smob. + However, this is not sufficient when classes with their own + protectable elements are derived from the Complex base class. This + is because initialization order is a tricky thing: once a base class + calls smobify_self () in its constructor, further allocations during + construction of base class and derived classes might lead to + mark_smob calls on the object under construction. When those call a + virtual function like derived_mark, the virtual function + corresponding to the incompletely initialized object of derived + class type is likely to be called. + + The order of initialization of an object consists in calling the + constructors of virtual base classes, then of non-virtual base + classes, then initializing all data members. + + As a result, the constructor of a derived class comes too late for + initialization of data members that may be accessed in the + derived_mark kind of functions. + + Such data members are consequently moved into Preinit_* classes + which come before the smobifying base class in derivation order and + construct the contained data members in a state suitable for + derived_mark calls. + + CALLING INTERFACE - Common public methods to C++ smob objects: + Common global functions for accessing C++ smob objects: - - unsmob (SCM x) - unpacks X and returns pointer to the C++ object, - or 0 if it has the wrong type. This can be used as a boolean - condition at C++ level. - - smob_p (SCM x) returns #t or #f at Scheme level. + - unsmob (SCM x) - unpack X and return a pointer to the C++ object, + or 0 if it has the wrong type. IMPLEMENTATION @@ -121,7 +144,7 @@ debugging purposes. If the class does not define this function, the output will be # when printing. - - a static const type_p_name_[] string set to something like + - a static const * const type_p_name_ string set to something like "ly:grob?". When provided, an accordingly named function for checking for the given smob type will be available in Scheme. @@ -134,12 +157,15 @@ // class) to make sure the variable is actually instantiated. class Scm_init { + static const Scm_init * list_; + void (*const fun_)(void); + Scm_init const * const next_; + Scm_init (); // don't use default constructor, don't define + Scm_init (const Scm_init &); // don't define copy constructor public: - Scm_init () { } - Scm_init (void (*fun) (void)) - { - add_scm_init_func (fun); - } + Scm_init (void (*fun) (void)) : fun_ (fun), next_ (list_) + { list_ = this; } + static void init (); }; template @@ -149,11 +175,11 @@ class Smob_base static Scm_init scm_init_; static void init (void); static string smob_name_; +protected: static Super *unchecked_unsmob (SCM s) { return reinterpret_cast (SCM_SMOB_DATA (s)); } -protected: // reference scm_init_ in smob_tag which is sure to be called. The // constructor, in contrast, may not be called at all in classes // like Smob1. @@ -175,48 +201,62 @@ private: // Most default functions are do-nothings. void init() will // recognize their address when not overriden and will then refrain // altogether from passing the the respective callbacks to GUILE. - SCM mark_smob (void); + + SCM mark_smob (void) const; static SCM mark_trampoline (SCM); // Used for calling mark_smob static size_t free_smob (SCM obj); static SCM equal_p (SCM, SCM); - int print_smob (SCM, scm_print_state *); + int print_smob (SCM, scm_print_state *) const; static int print_trampoline (SCM, SCM, scm_print_state *); + static void smob_proc_init (scm_t_bits) { }; - // type_p_name_ can be overriden in the Super class with a static - // const char [] string. This requires both a declaration in the - // class as well as a single instantiation outside. Using a - // template specialization for supplying a different string name - // right in Smob_base itself seems tempting, but the C++ - // rules would then require a specialization declaration at the - // class definition site as well as a specialization instantiation - // in a single compilation unit. That requires just as much source - // code maintenance while being harder to understand and quite - // trickier in its failure symptoms when things go wrong. So we - // just use a static zero as "not here" indication. - static const int type_p_name_ = 0; + // Define type_p_name_ in the Super class as a const char * const. + // Without such definition it defaults to 0, producing no predicate. + + static const char * const type_p_name_; // = 0 // LY_DECLARE_SMOB_PROC is used in the Super class definition for - // making a smob callable like a function. Declaration has to be - // public. It may be either be completed with a semicolon in which - // case a definition of the member function smob_proc has to be done - // outside of the class body, or the semicolon is left off and an - // inline function body is added immediately below. It would be - // nice if this were a non-static member function but it would seem - // tricky to do the required trampolining for unsmobbing the first - // argument of the callback and using it as a this pointer. -#define LY_DECLARE_SMOB_PROC(REQ, OPT, VAR, ARGLIST) \ - static const int smob_proc_signature_ = ((REQ)<<8)|((OPT)<<4)|(VAR); \ - static SCM smob_proc ARGLIST - - // a separate LY_DEFINE_SMOB_PROC seems sort of pointless as it - // would just result in SCM CLASS::smob_proc ARGLIST - // - // The default case without function functionality is recognized by - // smob_proc_signature being -1. - static const int smob_proc = 0; - static const int smob_proc_signature_ = -1; + // making a smob callable like a function. Its first argument is a + // function member pointer constant, to a function taking the + // correct number of SCM arguments and returning SCM. The function + // itself has to be defined separately. + +#define LY_DECLARE_SMOB_PROC(PMF, REQ, OPT, VAR) \ + static void smob_proc_init (scm_t_bits smob_tag) \ + { \ + scm_set_smob_apply (smob_tag, \ + (scm_t_subr)smob_trampoline, \ + REQ, OPT, VAR); \ + } + + // Well, function template argument packs are a C++11 feature. So + // we just define a bunch of trampolines manually. It turns out + // that GUILE 1.8.8 cannot actually make callable structures with + // more than 3 arguments anyway. That's surprising, to say the + // least, but in emergency situations one can always use a "rest" + // argument and take it apart manually. + + template + static SCM smob_trampoline (SCM self) + { + return (Super::unchecked_unsmob (self)->*pmf)(); + } + template + static SCM smob_trampoline (SCM self, SCM arg1) + { + return (Super::unchecked_unsmob (self)->*pmf)(arg1); + } + template + static SCM smob_trampoline (SCM self, SCM arg1, SCM arg2) + { + return (Super::unchecked_unsmob (self)->*pmf)(arg1, arg2); + } + template + static SCM smob_trampoline (SCM self, SCM arg1, SCM arg2, SCM arg3) + { + return (Super::unchecked_unsmob (self)->*pmf)(arg1, arg2, arg3); + } -public: static bool is_smob (SCM s) { return SCM_SMOB_PREDICATE (smob_tag (), s); @@ -225,12 +265,20 @@ public: { return is_smob (s) ? SCM_BOOL_T : SCM_BOOL_F; } - static Super *unsmob (SCM s) - { - return is_smob (s) ? Super::unchecked_unsmob (s) : 0; - } + + template + friend T *unsmob (SCM s); + + template + friend T *ly_assert_smob (SCM s, int number, const char *fun); }; +template +inline T *unsmob (SCM s) +{ + return T::is_smob (s) ? dynamic_cast (T::unchecked_unsmob (s)) : 0; +} + // Simple smobs template class Simple_smob : public Smob_base { @@ -250,14 +298,25 @@ public: void protect_smob (SCM smob, SCM *prot_cons); void unprotect_smob (SCM smob, SCM *prot_cons); +// The Smob_core class is not templated and contains material not +// depending on the Super class. + +class Smob_core { +protected: + SCM self_scm_; + Smob_core () : self_scm_ (SCM_UNDEFINED) { }; +public: + SCM self_scm () const { return self_scm_; } + Listener get_listener (SCM callback); +}; + template -class Smob : public Smob_base { +class Smob : public Smob_core, public Smob_base { private: - SCM self_scm_; SCM protection_cons_; Smob (const Smob &); // Do not define! Not copyable! protected: - Smob () : self_scm_ (SCM_UNDEFINED), protection_cons_ (SCM_EOL) { }; + Smob () : protection_cons_ (SCM_EOL) { }; public: static size_t free_smob (SCM obj) { @@ -283,7 +342,6 @@ public: unprotect_smob (s, &protection_cons_); return s; } - SCM self_scm () const { return self_scm_; } }; extern bool parsed_objects_should_be_dead; @@ -311,7 +369,7 @@ public: // least some, so they are apparently not protected in spite of being // included in the GC scans. So it would appear that scanning smobs // is not equivalent to marking them. Ugh. -#if !defined(NDEBUG) && !GUILEV2 +#if defined(DEBUG) && !GUILEV2 #define ASSERT_LIVE_IS_ALLOWED(arg) \ do { \ static parsed_dead pass_here; \