#define SMOBS_HH
#include "lily-guile.hh"
+#include "lily-proto.hh"
#include "warn.hh"
#include <string>
Complex smobs are created by deriving from Smob<Classname>.
+ 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<T> (SCM x) - unpack X and return a pointer to the C++ object,
+ or 0 if it has the wrong type.
IMPLEMENTATION
debugging purposes. If the class does not define this function,
the output will be #<Classname> 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.
static Scm_init scm_init_;
static void init (void);
static string smob_name_;
+protected:
static Super *unchecked_unsmob (SCM s)
{
return reinterpret_cast<Super *> (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.
// 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) { };
+
+ // Define type_p_name_ in the Super class as a const char * const.
+ // Without such definition it defaults to 0, producing no predicate.
- // 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<Super> 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;
+ 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<PMF>, \
+ 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 <SCM (Super::*pmf)(void)>
+ static SCM smob_trampoline (SCM self)
+ {
+ return (Super::unchecked_unsmob (self)->*pmf)();
+ }
+ template <SCM (Super::*pmf)(SCM)>
+ static SCM smob_trampoline (SCM self, SCM arg1)
+ {
+ return (Super::unchecked_unsmob (self)->*pmf)(arg1);
+ }
+ template <SCM (Super::*pmf)(SCM, SCM)>
+ static SCM smob_trampoline (SCM self, SCM arg1, SCM arg2)
+ {
+ return (Super::unchecked_unsmob (self)->*pmf)(arg1, arg2);
+ }
+ template <SCM (Super::*pmf)(SCM, SCM, SCM)>
+ 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);
{
return is_smob (s) ? SCM_BOOL_T : SCM_BOOL_F;
}
- static Super *unsmob (SCM s)
- {
- return is_smob (s) ? Super::unchecked_unsmob (s) : 0;
- }
-};
-// derived_unsmob includes a dynamic_cast:
+ template <class T>
+ friend T *unsmob (SCM s);
+
+ template <class T>
+ friend T *ly_assert_smob (SCM s, int number, const char *fun);
+};
template <class T>
-inline T *derived_unsmob (SCM arg)
+inline T *unsmob (SCM s)
{
- return dynamic_cast<T *> (T::unsmob (arg));
+ return T::is_smob (s) ? dynamic_cast<T *> (T::unchecked_unsmob (s)) : 0;
}
// Simple smobs
Smob_core () : self_scm_ (SCM_UNDEFINED) { };
public:
SCM self_scm () const { return self_scm_; }
+ Listener get_listener (SCM callback);
};
template <class Super>
// 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; \