From 076cead5e8281361b07d827a7143395583c2401e Mon Sep 17 00:00:00 2001 From: David Daniel Date: Thu, 12 Feb 2004 21:30:06 +0000 Subject: [PATCH] Changing the object system (again) to transparently run the constructor and destructor hierarchy. This commit was SVN r748. --- src/lam/lfc/lam_object.c | 80 ++++++-- src/lam/lfc/lam_object.h | 420 ++++++++++++++++++++++++--------------- 2 files changed, 327 insertions(+), 173 deletions(-) diff --git a/src/lam/lfc/lam_object.c b/src/lam/lfc/lam_object.c index e0ed809f01..fa2e851ace 100644 --- a/src/lam/lfc/lam_object.c +++ b/src/lam/lfc/lam_object.c @@ -3,29 +3,75 @@ */ /** - * @file Implementation of lam_object_t, the base lam foundation class + * @file + * + * Implementation of lam_object_t, the base lam foundation class */ +#include + +#include "lam/constants.h" #include "lam/lfc/lam_object.h" -static void lam_object_construct(lam_object_t * obj) -{ - obj->obj_reference_count = 1; -} - - -static void lam_object_destruct(lam_object_t * obj) -{ - /* Move along, nothing to see here! */ -} - - +/* + * Instantiation of class descriptor for the base class. This is + * special, since be mark it as already initialized, with no parent + * and no constructor or desctructor + */ lam_class_info_t lam_object_t_class_info = { - "lam_object_t", - 0, - lam_object_construct, - lam_object_destruct + "lam_object_t", /* name */ + NULL, /* parent class */ + NULL, /* constructor */ + NULL, /* destructor */ + 1, /* initialized -- this class is preinitialized */ + 0, /* class hierarchy depth */ + NULL, /* array of constructors */ + NULL /* array of destructors */ }; +/* + * Lazy initialization of class descriptor. + */ +void lam_class_initialize(lam_class_info_t *class) +{ + lam_class_info_t *c; + int i; + assert(class); + assert(0 == class->cls_initialized); + + /* + * First calculate depth of class hierarchy + */ + + class->cls_depth = 0; + for (c = class; c; c = c->cls_parent) { + class->cls_depth += 1; + } + + /* + * Allocate arrays for hierarchy of constructors and destructors + */ + + class->cls_construct_array = malloc(class->cls_depth * + sizeof(lam_construct_t)); + if (NULL == class->cls_construct_array) { + perror("Out of memory"); + } + + class->cls_destruct_array = malloc(class->cls_depth * + sizeof(lam_destruct_t)); + if (NULL == class->cls_destruct_array) { + perror("Out of memory"); + } + + c = class; + for (i = 0; i < class->cls_depth; i++) { + class->cls_construct_array[i] = c->cls_construct; + class->cls_destruct_array[i] = c->cls_destruct; + c = c->cls_parent; + } + + class->cls_initialized = 1; +} diff --git a/src/lam/lfc/lam_object.h b/src/lam/lfc/lam_object.h index ba909bddac..1c19890bd1 100644 --- a/src/lam/lfc/lam_object.h +++ b/src/lam/lfc/lam_object.h @@ -8,18 +8,18 @@ * Simple C-language object-oriented system with single inheritance * and ownership-based memory management using a retain/release model. * - * A class consists of a struct and singly-instantiated "class info" + * A class consists of a struct and singly-instantiated class * descriptor. The first element of the struct must be the parent * class's struct. The class descriptor must be given a well-known * name based upon the class struct name: If the struct is sally_t, - * the class info descriptor should be xxx_t_class_info. + * the class descriptor should be sally_t_class. * * (a) To define a class * * In a interface (.h) file, define the class. The first element * should always be the parent class, and be called "super", * for example - * + * @code * typedef struct sally_t sally_t; * struct sally_t * { @@ -28,21 +28,21 @@ * ... * }; * - * extern lam_class_info_t sally_t_class_info; - * + * extern lam_class_info_t sally_t_class; + * @endcode * All classes must have a parent. * - * In an implementation (.c) file, instantiate a class info descriptor - * for this class, and should be the name of the class with - * "_class_info" appended: - * - * lam_class_info_t sally_t_class_info = { + * In an implementation (.c) file, instantiate a class descriptor for + * this class, and should be the name of the class with "_class" + * appended: + * @code + * lam_class_info_t sally_t_class = { * "sally_t", - * CLASS_INFO(parent_t), // pointer to parent_t_class_info + * CLASS_INFO(parent_t), // pointer to parent_t_class * sally_construct, * sally_destruct * }; - * + * @endcode * This variable should be publically advertised using the "extern" * statement in the interface file as shown above. * @@ -50,34 +50,24 @@ * constructor and destructor for the class and are best defined as * static functions in the implementation file. * - * The first thing sally_construct should do is run its parent's - * constructor using the OBJ_CONSTRUCT_SUPER macro: - * - * OBJ_CONSTRUCT_SUPER(obj, type_of_parent); - * - * Similarly, the las thing sally_destruct should do is run its - * parents' destructor. - * - * OBJ_DESTRUCT_SUPER(obj, type_of_parent); - * * Other class methods may be added to the struct. * * (b) Class instantiation: dynamic * * To create a instance of a class (an object) use OBJ_NEW: - * + * @code * sally_t *sally = OBJ_NEW(sally_t); - * + * @endcode * which allocates memory of sizeof(sally_t) and runs the class's * "init" method. * * Use OBJ_RETAIN, OBJ_RELEASE to do reference-count-based * memory management: - * + * @code * OBJ_RETAIN(sally); * OBJ_RELEASE(sally); * OBJ_RELEASE(sally); - * + * @endcode * When the reference count reaches zero, the class's "fini" method * is run and the memory is freed. * @@ -89,57 +79,258 @@ * For an object with static (or stack) allocation, it is only * necessary to initialize the memory, which is done using * OBJ_CONSTRUCT: - * + * @code * sally_t sally; * * OBJ_CONSTRUCT(&sally, sally_t); - * + * @endcode * The retain/release model is not necessary here, but before the * object goes out of scope, OBJ_DESTRUCT should be run to release * initialized resources: - * + * @code * OBJ_DESTRUCT(&sally); + * @endcode */ #ifndef LAM_OBJECT_H #define LAM_OBJECT_H +#include #include +#include "lam_config.h" #include "lam/types.h" #include "lam/atomic.h" #include "lam/mem/malloc.h" /* - * Class definition + * BEGIN_C_DECLS should be used at the beginning of your declarations, + * so that C++ compilers don't mangle their names. Use END_C_DECLS at + * the end of C declarations. */ +#undef BEGIN_C_DECLS +#undef END_C_DECLS +#ifdef __cplusplus +# define BEGIN_C_DECLS extern "C" { +# define END_C_DECLS } +#else +# define BEGIN_C_DECLS /* empty */ +# define END_C_DECLS /* empty */ +#endif + + +/* typedefs ***********************************************************/ typedef struct lam_object_t lam_object_t; typedef struct lam_class_info_t lam_class_info_t; typedef void (*lam_construct_t) (lam_object_t *); typedef void (*lam_destruct_t) (lam_object_t *); -struct lam_class_info_t { - const char *cls_name; - lam_class_info_t *cls_parent; - lam_construct_t cls_construct; - lam_destruct_t cls_destruct; -}; -extern lam_class_info_t lam_object_t_class_info; +/* types **************************************************************/ + +/** + * Class descriptor. + * + * There should be a single instance of this descriptor for each class + * definition. + */ +struct lam_class_info_t { + const char *cls_name; /**< symbolic name for class */ + lam_class_info_t *cls_parent; /**< parent class descriptor */ + lam_construct_t cls_construct; /**< class constructor */ + lam_destruct_t cls_destruct; /**< class destructor */ + int cls_initialized; /**< is class initialized */ + int cls_depth; /**< depth of class hierarchy tree */ + lam_construct_t *cls_construct_array; + /**< array of parent class descriptors */ + lam_destruct_t *cls_destruct_array; + /**< array of parent class descriptors */ +}; /** - * Base object for the class system. This is special and does not - * follow the pattern for other classes. + * Base object. + * + * This is special and does not follow the pattern for other classes. */ struct lam_object_t { - lam_class_info_t *obj_class_info; /**< class information */ - int obj_reference_count; /**< reference count for the class */ + lam_class_info_t *obj_class_info; /**< class descriptor */ + int obj_reference_count; /**< reference count */ }; -/* Inline functions and prototypes *******************************/ +/* macros ************************************************************/ + +/** + * Return a pointer to the class descriptor associated with a + * class type. + * + * @param type Name of class + * @return Pointer to class descriptor + */ +#define CLASS_INFO(type) (&(type ## _class_info)) + + +/** + * Static initializer for a class descriptor + * + * @param NAME Symbolic name for class + * @param PARENT Type of parent class + * @param CONSTRUCTOR Pointer to constructor + * @param DESTRUCTOR Pointer to destructor + * @return Static initializer string + */ +#define CLASS_INITIALIZE(NAME, PARENT, CONSTRUCTOR, DESTRUCTOR) \ + { NAME, PARENT, CONSTRUCTOR, DESTRUCTOR, 0, 0, NULL, NULL } + + +/** + * Create an object: dynamically allocate storage and run the class + * constructor. + * + * @param type Type (class) of the object + * @return Pointer to the object + */ +#define OBJ_NEW(type) \ + ((type *) lam_obj_new(sizeof(type), CLASS_INFO(type))) + + +/** + * Retain an object (by incrementing its reference count) + * + * @param object Pointer to the object + */ +#define OBJ_RETAIN(object) \ + do { \ + if (object) { \ + lam_obj_retain((lam_object_t *) object); \ + } \ + } while (0) + + +/** + * Release an object (by decrementing its reference count). If the + * reference count reaches zero, destruct (finalize) the object and + * free its storage. + * + * @param object Pointer to the object + */ +#define OBJ_RELEASE(object) \ + do { \ + if (object) { \ + lam_obj_release((lam_object_t *) object); \ + } \ + } while (0) + + +/** + * Construct (initialize) objects that are not dynamically allocated. + * + * @param object Pointer to the object + * @param type The object type + */ +#define OBJ_CONSTRUCT(object, type) \ + do { \ + if (0 == CLASS_INFO(type)->cls_initialized) { \ + lam_class_initialize(CLASS_INFO(type)); \ + } \ + if (object) { \ + ((lam_object_t *) object)->obj_class_info = CLASS_INFO(type); \ + ((lam_object_t *) object)->obj_reference_count = 1; \ + lam_obj_run_constructors((lam_object_t *) object); \ + } \ + } while (0) + + +/** + * Destruct (finalize) an object that is not dynamically allocated. + * + * @param object Pointer to the object + */ +#define OBJ_DESTRUCT(object) \ + do { \ + if (object) { \ + lam_obj_run_destructors((lam_object_t *) object); \ + } \ + } while (0) + + +#define OBJ_CONSTRUCT_SUPER(A, B) +#define OBJ_DESTRUCT_SUPER(A, B) + + +/* declarations *******************************************************/ + +BEGIN_C_DECLS + +extern lam_class_info_t lam_object_t_class_info; + +/** + * Lazy initialization of class descriptor. + * + * Specifically cache arrays of function pointers for the constructor + * and destructor hierarchies for this class. + * + * @param class Pointer to class descriptor + */ +void lam_class_initialize(lam_class_info_t *); + +END_C_DECLS + +/** + * Run the hierarchy of class constructors for this object, in a + * parent-first order. + * + * Do not use this function directly: use OBJ_CONSTRUCT() instead. + * + * WARNING: This implementation relies on a hardwired maximum depth of + * the inheritance tree!!! + * + * Hardwired for fairly shallow inheritance trees + * @param size Pointer to the object. + */ +static inline void lam_obj_run_constructors(lam_object_t *object) +{ + lam_class_info_t *cls; + int i; + + assert(NULL != object); + assert(NULL != object->obj_class_info); + + cls = object->obj_class_info; + for (i = cls->cls_depth - 1; i >= 0; i--) { + if (cls->cls_construct_array[i]) { + (cls->cls_construct_array[i])(object); + } + } +} + + +/** + * Run the hierarchy of class destructors for this object, in a + * parent-last order. + * + * Do not use this function directly: use OBJ_DESTRUCT() instead. + * + * @param size Pointer to the object. + */ +static inline void lam_obj_run_destructors(lam_object_t *object) +{ + lam_class_info_t *cls; + int i; + + assert(NULL != object); + assert(NULL != object->obj_class_info); + + cls = object->obj_class_info; + for (i = 0; i < cls->cls_depth; i++) { + if (cls->cls_destruct_array[i]) { + (cls->cls_destruct_array[i])(object); + } + } +} + /** * Create new object: dynamically allocate storage and run the class @@ -148,20 +339,26 @@ struct lam_object_t { * Do not use this function directly: use OBJ_NEW() instead. * * @param size Size of the object - * @param size Pointer to the class info struct for this class + * @param cls Pointer to the class descriptor of this object * @return Pointer to the object */ -static inline lam_object_t *lam_new(size_t size, - lam_class_info_t *class_info) +static inline lam_object_t *lam_obj_new(size_t size, + lam_class_info_t *cls) { - lam_object_t *obj = (lam_object_t *) malloc(size); - if (NULL != obj) { - obj->obj_class_info = class_info; - obj->obj_class_info->cls_construct(obj); + lam_object_t *object; + + assert(size >= sizeof(lam_object_t)); + + object = (lam_object_t *) malloc(size); + if (NULL != object) { + object->obj_class_info = cls; + object->obj_reference_count = 1; + lam_obj_run_constructors(object); } - return obj; + return object; } + /* * This function is used by inline functions later in this file, and * it must be defined by other header files later (eg., one of the @@ -175,11 +372,16 @@ static inline int fetchNadd(volatile int *addr, int inc); * * Do not use this function directly: use OBJ_RETAIN instead. * - * @param obj Pointer to the object + * @param object Pointer to the object */ -static inline void lam_obj_retain(lam_object_t *obj) +static inline void lam_obj_retain(lam_object_t *object) { - fetchNadd(&obj->obj_reference_count, 1); + assert(NULL != object); + assert(NULL != object->obj_class_info); + + fetchNadd(&(object->obj_reference_count), 1); + + assert(object->obj_reference_count >= 0); } @@ -190,113 +392,19 @@ static inline void lam_obj_retain(lam_object_t *obj) * * Do not use this function directly: use OBJ_RELEASE instead. * - * @param obj Pointer to the object + * @param object Pointer to the object */ -static inline void lam_obj_release(lam_object_t *obj) +static inline void lam_obj_release(lam_object_t *object) { - if (fetchNadd(&obj->obj_reference_count, -1) == 1) { - obj->obj_class_info->cls_destruct(obj); - free(obj); + assert(NULL != object); + assert(NULL != object->obj_class_info); + + if (fetchNadd(&object->obj_reference_count, -1) == 1) { + object->obj_class_info->cls_destruct(object); + free(object); } } - -/* - * Macros - */ - -/** - * Return a pointer to the class info descriptor associated with a - * class type. - * - * @param type Name of class - * @return Pointer to class info descriptor - */ -#define CLASS_INFO(type) (&(type ## _class_info)) - - -/** - * Create an object: dynamically allocate storage and run the class - * constructor. - * - * @param type Type (class) of the object - * @return Pointer to the object - */ -#define OBJ_NEW(type) \ - ((type *) lam_new(sizeof(type), CLASS_INFO(type))) - - -/** - * Retain an object (by incrementing its reference count) - * - * @param obj Pointer to the object - */ -#define OBJ_RETAIN(obj) \ - do { \ - if (obj) lam_obj_retain((lam_object_t *) obj); \ - } while (0) - - -/** - * Release an object (by decrementing its reference count). If the - * reference count reaches zero, destruct (finalize) the object and - * free its storage. - * - * @param obj Pointer to the object - */ -#define OBJ_RELEASE(obj) \ - do { \ - if (obj) lam_obj_release((lam_object_t *) obj); \ - } while (0) - - -/** - * Construct (initialize) objects that are not dynamically allocated. - * - * @param obj Pointer to the object - * @param type The object type - */ -#define OBJ_CONSTRUCT(obj, type) \ - do { \ - ((lam_object_t *) obj)->obj_class_info = CLASS_INFO(type); \ - ((lam_object_t *) obj) \ - ->obj_class_info->cls_construct((lam_object_t *) obj); \ - } while (0) - - -/** - * Destruct (finalize) an object that is not dynamically allocated. - * - * @param obj Pointer to the object - */ -#define OBJ_DESTRUCT(obj) \ - do { \ - ((lam_object_t *) obj) \ - ->obj_class_info->cls_destruct((lam_object_t *) obj); \ - } while (0) - - -/** - * Construct (initialize) the parent object - * - * @param obj Pointer to the object - * @param super_type Type of the parent object - */ -#define OBJ_CONSTRUCT_SUPER(obj, super_type) \ - do { \ - CLASS_INFO(super_type)->cls_construct((lam_object_t *) obj); \ - } while (0) - - -/** - * Destruct (finalize) the parent object. - * - * @param obj Pointer to the object - * @param super_type Type of the parent object - */ -#define OBJ_DESTRUCT_SUPER(obj, super_type) \ - do { \ - CLASS_INFO(super_type)->cls_destruct((lam_object_t *) obj); \ - } while (0) +/**********************************************************************/ #endif /* LAM_OBJECT_H */