/* GTS - Library for the manipulation of triangulated surfaces * Copyright (C) 1999 Stéphane Popinet * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "gts.h" #include "gts-private.h" static GHashTable * class_table = NULL; static void gts_object_class_init (GtsObjectClass * klass, GtsObjectClass * parent_class) { if (parent_class) { gts_object_class_init (klass, parent_class->parent_class); if (parent_class->info.class_init_func) (*parent_class->info.class_init_func) (klass); } } /** * gts_object_class_new: * @parent_class: a #GtsObjectClass. * @info: a #GtsObjectClassInfo, description of the new class to create. * * Returns: a new #GtsObjectClass derived from @parent_class and described by * @info. */ gpointer gts_object_class_new (GtsObjectClass * parent_class, GtsObjectClassInfo * info) { GtsObjectClass * klass; g_return_val_if_fail (info != NULL, NULL); g_return_val_if_fail (parent_class == NULL || info->object_size >= parent_class->info.object_size, NULL); g_return_val_if_fail (parent_class == NULL || info->class_size >= parent_class->info.class_size, NULL); klass = g_malloc0 (info->class_size); klass->info = *info; klass->parent_class = parent_class; gts_object_class_init (klass, klass); if (!class_table) class_table = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (class_table, klass->info.name, klass); return klass; } /** * gts_object_class_from_name: * @name: the name of a #GtsObjectClass. * * Returns: the #GtsObjectClass with name @name or %NULL if it hasn't been * instantiated yet. */ GtsObjectClass * gts_object_class_from_name (const gchar * name) { g_return_val_if_fail (name != NULL, NULL); if (!class_table) return NULL; return g_hash_table_lookup (class_table, name); } static void object_destroy (GtsObject * object) { #ifdef DEBUG_IDENTITY #ifdef DEBUG_LEAKS fprintf (stderr, "destroy %s %p->%d\n", object->klass->info.name, object, id (object)); #endif id_remove (object); #endif object->klass = NULL; g_free (object); } static void object_clone (GtsObject * clone, GtsObject * object) { memcpy (clone, object, object->klass->info.object_size); clone->reserved = NULL; } static void object_class_init (GtsObjectClass * klass) { klass->clone = object_clone; klass->destroy = object_destroy; klass->read = NULL; klass->write = NULL; klass->color = NULL; klass->attributes = NULL; } static void object_init (GtsObject * object) { object->reserved = NULL; object->flags = 0; } /** * gts_object_class: * * Returns: the #GtsObjectClass. */ GtsObjectClass * gts_object_class (void) { static GtsObjectClass * klass = NULL; if (klass == NULL) { GtsObjectClassInfo object_info = { "GtsObject", sizeof (GtsObject), sizeof (GtsObjectClass), (GtsObjectClassInitFunc) object_class_init, (GtsObjectInitFunc) object_init, (GtsArgSetFunc) NULL, (GtsArgGetFunc) NULL }; klass = gts_object_class_new (NULL, &object_info); } return klass; } /** * gts_object_check_cast: * @object: a #GtsObject. * @klass: a #GtsObjectClass. * * Returns: @object while emitting warnings if @object is not of class @klass. */ gpointer gts_object_check_cast (gpointer object, gpointer klass) { if (!object) { g_warning ("invalid cast from (NULL) pointer to `%s'", GTS_OBJECT_CLASS (klass)->info.name); return object; } if (!((GtsObject *) object)->klass) { g_warning ("invalid unclassed pointer in cast to `%s'", GTS_OBJECT_CLASS (klass)->info.name); return object; } if (!gts_object_is_from_class (object, klass)) { g_warning ("invalid cast from `%s' to `%s'", ((GtsObject *) object)->klass->info.name, GTS_OBJECT_CLASS (klass)->info.name); return object; } return object; } /** * gts_object_class_check_cast: * @klass: a #GtsObjectClass. * @from: a #GtsObjectClass. * * Returns: @klass while emitting warnings if @klass is not derived from * @from. */ gpointer gts_object_class_check_cast (gpointer klass, gpointer from) { if (!klass) { g_warning ("invalid cast from (NULL) pointer to `%s'", GTS_OBJECT_CLASS (from)->info.name); return klass; } if (!gts_object_class_is_from_class (klass, from)) { g_warning ("invalid cast from `%s' to `%s'", GTS_OBJECT_CLASS (klass)->info.name, GTS_OBJECT_CLASS (from)->info.name); return klass; } return klass; } /** * gts_object_init: * @object: a #GtsObject. * @klass: a #GtsObjectClass. * * Calls the init method of @klass with @object as argument. This is done * recursively in the correct order (from the base class to the top). You * should rarely need this function as it is called automatically by the * constructor for each class. */ void gts_object_init (GtsObject * object, GtsObjectClass * klass) { GtsObjectClass * parent_class; g_return_if_fail (object != NULL); g_return_if_fail (klass != NULL); parent_class = klass->parent_class; if (parent_class) gts_object_init (object, parent_class); if (klass->info.object_init_func) (*klass->info.object_init_func) (object); } /** * gts_object_new: * @klass: a #GtsObjectClass. * * Returns: a new initialized object of class @klass. */ GtsObject * gts_object_new (GtsObjectClass * klass) { GtsObject * object; g_return_val_if_fail (klass != NULL, NULL); object = g_malloc0 (klass->info.object_size); object->klass = klass; gts_object_init (object, klass); #ifdef DEBUG_IDENTITY id_insert (object); #ifdef DEBUG_LEAKS fprintf (stderr, "new %s %p->%d\n", klass->info.name, object, id (object)); #endif #endif return object; } /** * gts_object_clone: * @object: a #GtsObject. * * Calls the clone method of @object. The call to this function will fail * if no clone method exists for the given object. * * Returns: a new object clone of @object. */ GtsObject * gts_object_clone (GtsObject * object) { GtsObject * clone; g_return_val_if_fail (object != NULL, NULL); g_return_val_if_fail (object->klass->clone, NULL); clone = g_malloc0 (object->klass->info.object_size); clone->klass = object->klass; object_init (clone); (* object->klass->clone) (clone, object); #ifdef DEBUG_IDENTITY id_insert (clone); #ifdef DEBUG_LEAKS fprintf (stderr, "clone %s %p->%d\n", clone->klass->info.name, clone, id (clone)); #endif #endif return clone; } /** * gts_object_destroy: * @object: a #GtsObject. * * Calls the destroy method of @object, freeing all memory allocated for it. */ void gts_object_destroy (GtsObject * object) { g_assert (object->klass->destroy); GTS_OBJECT_SET_FLAGS (object, GTS_DESTROYED); (* object->klass->destroy) (object); } /** * gts_object_reset_reserved: * @object: a #GtsObject. * * Reset the reserved field of @object. */ void gts_object_reset_reserved (GtsObject * object) { g_return_if_fail (object != NULL); object->reserved = NULL; } /** * gts_object_attributes: * @object: a #GtsObject. * @from: a #GtsObject. * * Calls the attributes() method of @object using @from as source. */ void gts_object_attributes (GtsObject * object, GtsObject * from) { g_return_if_fail (object != NULL); if (object->klass->attributes) (* object->klass->attributes) (object, from); } static void free_class (gchar * name, GtsObjectClass * klass) { g_free (klass); } /** * gts_finalize: * * Free all the memory allocated by the object system of GTS. No other * GTS function can be called after this function has been called. */ void gts_finalize (void) { if (class_table) { g_hash_table_foreach (class_table, (GHFunc) free_class, NULL); g_hash_table_destroy (class_table); class_table = NULL; } }