/*----------------------------------------------------------------- LOG GEM - Graphics Environment for Multimedia The base class for all externs written in C++ Copyright (c) 1997-1999 Mark Danks. mark@danks.org For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution. -----------------------------------------------------------------*/ #ifndef _INCLUDE__GEM_BASE_CPPEXTERN_H_ #define _INCLUDE__GEM_BASE_CPPEXTERN_H_ #include "Gem/ExportDef.h" #include "Gem/RTE.h" #include "Gem/Version.h" #include #include class CPPExtern; /* forward declaration of a generic exception handler for GemExceptions */ namespace gem { GEM_EXTERN void catchGemException(const char*objname, const t_object*obj); }; /*----------------------------------------------------------------- ------------------------------------------------------------------- CLASS Obj_header The obligatory object header DESCRIPTION This is in a separate struct to assure that when PD uses the class, the t_object is the very first thing. If it were the first thing in CPPExtern, then there could be problems with the vtable. -----------------------------------------------------------------*/ struct GEM_EXTERN Obj_header { ////////// // The obligatory object header t_object pd_obj; ////////// // Our data structure CPPExtern *data; // This has a dummy arg so that NT won't complain void *operator new(size_t, void *location, void *dummy); }; /*----------------------------------------------------------------- ------------------------------------------------------------------- CLASS CPPExtern The base class for all externs written in C++ DESCRIPTION Each extern which is written in C++ needs to use the #defines at the end of this header file. Currently, the operator new(size_t) and operator delete(void *) are not overridden. This will be a problem when PD expects everything to fit in its memory space and control all memory allocation. The define CPPEXTERN_HEADER(NEW_CLASS, PARENT_CLASS); should be somewhere in your header file. One of the defines like CPPEXTERN_NEW(NEW_CLASS); CPPEXTERN_NEW_WITH_TWO_ARGS(NEW_CLASS, t_floatarg, A_FLOAT, t_floatarg, A_FLOAT); should be the first thing in your implementation file. NEW_CLASS is the name of your class and PARENT_CLASS is the parent of your class. -----------------------------------------------------------------*/ class GEM_EXTERN CPPExtern { public: ////////// // Constructor CPPExtern(void); ////////// // The Pd header t_object *x_obj; ////////// // Destructor virtual ~CPPExtern(void) = 0; ////////// // Get the object's canvas const t_canvas *getCanvas(void) const { return(m_canvas); } ////////// // This is a holder - don't touch it static t_object *m_holder; ////////// // my name static const char *m_holdname; t_symbol *m_objectname; protected: ////////// // Creation callback static void real_obj_setupCallback(t_class *) {} /////////// // called directly before the destructor // normally you should not override this (use the dtor!) // if you do override this, make sure that you call the parent as well virtual void beforeDeletion(); private: ////////// // The canvas that the object is in t_canvas *m_canvas; public: // these call pd's print-functions, and eventually prepend the object's name void startpost(const char*format, ...) const; void post(const char*format, ...) const; void endpost(void) const; void verbose(const int level, const char*format, ...) const; void error(const char*format, ...) const; /* internally uses pd_error() */ // searches for a file based on the parent abstraction's path // wraps open_via_path() and canvas_makefilename() // the full filename is returned // if the file does not exist, it is constructed std::string findFile(const std::string&filename, const std::string&ext) const; std::string findFile(const std::string&filename) const; private: mutable bool m_endpost; /* internal state for startpost/post/endpost */ static bool checkGemVersion(const int major, const int minor); CPPExtern(const CPPExtern&); virtual CPPExtern&operator=(const CPPExtern&); }; //////////////////////////////////////// // This should be used in the header //////////////////////////////////////// #define CPPEXTERN_HEADER(NEW_CLASS, PARENT_CLASS) \ public: \ static void obj_freeCallback(void *data) \ { CPPExtern *mydata = ((Obj_header *)data)->data; \ GetMyClass(data)->beforeDeletion(); \ delete mydata; \ ((Obj_header *)data)->Obj_header::~Obj_header(); } \ static void real_obj_setupCallback(t_class *classPtr) \ { PARENT_CLASS::real_obj_setupCallback(classPtr); \ NEW_CLASS::obj_setupCallback(classPtr); } \ private: \ static inline NEW_CLASS *GetMyClass(void *data) {return((NEW_CLASS *)((Obj_header *)data)->data);} \ static void obj_setupCallback(t_class *classPtr); //////////////////////////////////////// // This should be the first thing in the implementation file //////////////////////////////////////// // // NO ARGUMENTS ///////////////////////////////////////////////// #define CPPEXTERN_NEW(NEW_CLASS) \ REAL_NEW__CLASS(NEW_CLASS); \ static void* create_ ## NEW_CLASS (void) \ REAL_NEW__CREATE1(NEW_CLASS) \ obj->data = new NEW_CLASS(); \ REAL_NEW__CREATE2(NEW_CLASS) \ REAL_NEW__SETUP1(NEW_CLASS) \ REAL_NEW__SETUP2(NEW_CLASS) // // ONE ARGUMENT ///////////////////////////////////////////////// #define CPPEXTERN_NEW_WITH_ONE_ARG(NEW_CLASS, TYPE, PD_TYPE) \ REAL_NEW__CLASS(NEW_CLASS); \ static void* create_ ## NEW_CLASS (TYPE arg) \ REAL_NEW__CREATE1(NEW_CLASS) \ obj->data = new NEW_CLASS(arg); \ REAL_NEW__CREATE2(NEW_CLASS) \ REAL_NEW__SETUP1(NEW_CLASS) \ PD_TYPE, \ REAL_NEW__SETUP2(NEW_CLASS) // // GIMME ARGUMENT ///////////////////////////////////////////////// #define CPPEXTERN_NEW_WITH_GIMME(NEW_CLASS) \ REAL_NEW__CLASS(NEW_CLASS); \ static void* create_ ## NEW_CLASS (t_symbol*s, int argc, t_atom*argv) \ REAL_NEW__CREATE1(NEW_CLASS) \ obj->data = new NEW_CLASS(argc,argv); \ REAL_NEW__CREATE2(NEW_CLASS) \ REAL_NEW__SETUP1(NEW_CLASS) \ A_GIMME, \ REAL_NEW__SETUP2(NEW_CLASS) // // TWO ARGUMENTS ///////////////////////////////////////////////// #define CPPEXTERN_NEW_WITH_TWO_ARGS(NEW_CLASS, TYPE, PD_TYPE, TTWO, PD_TWO) \ REAL_NEW__CLASS(NEW_CLASS); \ static void* create_ ## NEW_CLASS (TYPE arg, TTWO arg2) \ REAL_NEW__CREATE1(NEW_CLASS) \ obj->data = new NEW_CLASS(arg, arg2); \ REAL_NEW__CREATE2(NEW_CLASS) \ REAL_NEW__SETUP1(NEW_CLASS) \ PD_TYPE, PD_TWO, \ REAL_NEW__SETUP2(NEW_CLASS) // // THREE ARGUMENTS ///////////////////////////////////////////////// #define CPPEXTERN_NEW_WITH_THREE_ARGS(NEW_CLASS, TYPE, PD_TYPE, TTWO, PD_TWO, TTHREE, PD_THREE) \ REAL_NEW__CLASS(NEW_CLASS); \ static void* create_ ## NEW_CLASS (TYPE arg, TTWO arg2, TTHREE arg3) \ REAL_NEW__CREATE1(NEW_CLASS) \ obj->data = new NEW_CLASS(arg, arg2, arg3); \ REAL_NEW__CREATE2(NEW_CLASS) \ REAL_NEW__SETUP1(NEW_CLASS) \ PD_TYPE, PD_TWO, PD_THREE, \ REAL_NEW__SETUP2(NEW_CLASS) // // FOUR ARGUMENTS ///////////////////////////////////////////////// #define CPPEXTERN_NEW_WITH_FOUR_ARGS(NEW_CLASS, TYPE, PD_TYPE, TTWO, PD_TWO, TTHREE, PD_THREE, TFOUR, PD_FOUR) \ REAL_NEW__CLASS(NEW_CLASS); \ static void* create_ ## NEW_CLASS (TYPE arg, TTWO arg2, TTHREE arg3, TFOUR arg4) \ REAL_NEW__CREATE1(NEW_CLASS) \ obj->data = new NEW_CLASS(arg, arg2, arg3, arg4); \ REAL_NEW__CREATE2(NEW_CLASS) \ REAL_NEW__SETUP1(NEW_CLASS) \ PD_TYPE, PD_TWO, PD_THREE, PD_FOUR, \ REAL_NEW__SETUP2(NEW_CLASS) // // FIVE ARGUMENTS ///////////////////////////////////////////////// #define CPPEXTERN_NEW_WITH_FIVE_ARGS(NEW_CLASS, TYPE, PD_TYPE, TTWO, PD_TWO, TTHREE, PD_THREE, TFOUR, PD_FOUR, TFIVE, PD_FIVE) \ REAL_NEW__CLASS(NEW_CLASS); \ static void* create_ ## NEW_CLASS (TYPE arg, TTWO arg2, TTHREE arg3, TFOUR arg4, TFIVE arg5) \ REAL_NEW__CREATE1(NEW_CLASS) \ obj->data = new NEW_CLASS(arg, arg2, arg3, arg4, arg5); \ REAL_NEW__CREATE2(NEW_CLASS) \ REAL_NEW__SETUP1(NEW_CLASS) \ PD_TYPE, PD_TWO, PD_THREE, PD_FOUR, PD_FIVE \ REAL_NEW__SETUP2(NEW_CLASS) ////////////////////////////////////////////////////////////////////////////// // These should never be called or used directly!!! // // /////////////////////////////////////////////////////////////////////////////// #define REAL_NEW__CLASS(NEW_CLASS) STATIC_CLASS t_class * NEW_CLASS ## _class #define REAL_NEW__CREATE1(NEW_CLASS) { \ try{ \ Obj_header *obj = new (pd_new(NEW_CLASS ## _class),(void *)NULL) Obj_header; \ CPPExtern::m_holder = &obj->pd_obj; \ CPPExtern::m_holdname=(char*)#NEW_CLASS; #define REAL_NEW__CREATE2(NEW_CLASS) \ CPPExtern::m_holder = NULL; \ CPPExtern::m_holdname=NULL; \ return(obj); \ } catch (...) {gem::catchGemException(CPPExtern::m_holdname, CPPExtern::m_holder); return NULL;} \ } #define REAL_NEW__SETUP1(NEW_CLASS) \ extern "C" { \ GEM_EXPORT void NEW_CLASS ## _setup(void) \ { \ static int recalled=0; if(recalled)return; recalled=1; \ NEW_CLASS ## _class = class_new( \ gensym(#NEW_CLASS), \ (t_newmethod)create_ ## NEW_CLASS, \ (t_method)&NEW_CLASS::obj_freeCallback, \ sizeof(Obj_header), GEM_CLASSFLAGS, #define REAL_NEW__SETUP2(NEW_CLASS) \ A_NULL); \ SET_HELPSYMBOL(NEW_CLASS); \ NEW_CLASS::real_obj_setupCallback(NEW_CLASS ## _class); \ } \ } \ AUTO_REGISTER_CLASS(NEW_CLASS); /////////////////////////////////////////////////////////////////////////////// // static class: // by default classes are declared static // however, sometimes we need classes not-static, so we can refer to them // from other classes /////////////////////////////////////////////////////////////////////////////// #ifdef NO_STATIC_CLASS # define STATIC_CLASS #else # define STATIC_CLASS static #endif /////////////////////////////////////////////////////////////////////////////// // auto registering a class // this creates a dummy class, whose constructor calls the setup-function // (registering the class with pd) // a static copy of this class is created at runtime, to actually do the setup-call /////////////////////////////////////////////////////////////////////////////// typedef void (*t_class_setup)(void); extern "C" { GEM_EXTERN void gem_register_class_setup(const char*name, t_class_setup); } #ifdef NO_AUTO_REGISTER_CLASS // if NO_AUTO_REGISTER_CLASS is defined, we will not register the class # define AUTO_REGISTER_CLASS(NEW_CLASS) \ static int NEW_CLASS ## _dummyinstance #else // for debugging we can show the which classes are auto-registering # define AUTO_REGISTER_CLASS(NEW_CLASS) \ class NEW_CLASS ## _cppclass { \ public: \ NEW_CLASS ## _cppclass(void) {gem_register_class_setup(#NEW_CLASS, & NEW_CLASS ## _setup); } \ }; \ static NEW_CLASS ## _cppclass NEW_CLASS ## _instance #endif /////////////////////////////////////////////////////////////////////////////// // setting the help-symbol /////////////////////////////////////////////////////////////////////////////// #if defined HELPSYMBOL_BASE || defined HELPSYMBOL # ifndef HELPSYMBOL_BASE # define HELPSYMBOL_BASE "" # endif # ifndef HELPSYMBOL # define SET_HELPSYMBOL(NEW_CLASS) \ class_sethelpsymbol(NEW_CLASS ## _class, gensym(HELPSYMBOL_BASE #NEW_CLASS)) # else # define SET_HELPSYMBOL(NEW_CLASS) \ class_sethelpsymbol(NEW_CLASS ## _class, gensym(HELPSYMBOL_BASE HELPSYMBOL)) # endif #else # define SET_HELPSYMBOL(NEW_CLASS) #endif /* HELPSYMBOL */ /////////////////////////////////////////////////////////////////////////////// // setting the class-flags /////////////////////////////////////////////////////////////////////////////// #ifndef GEM_CLASSFLAGS # define GEM_CLASSFLAGS 0 #endif // macros for boilerplate code to object messages #include "RTE/MessageCallbacks.h" #endif // for header file