/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* camel-object.c: Base class for Camel */ /* * Author: * Dan Winship * * Copyright 2000 Helix Code, Inc. (http://www.helixcode.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #ifdef HAVE_CONFIG_H #include #endif #include #include "camel-object.h" /* I just mashed the keyboard for these... */ #define CAMEL_OBJECT_MAGIC_VALUE 0x77A344EF #define CAMEL_OBJECT_CLASS_MAGIC_VALUE 0xEE26A990 #define CAMEL_OBJECT_FINALIZED_VALUE 0x84AC3656 #define CAMEL_OBJECT_CLASS_FINALIZED_VALUE 0x7621ABCD #define DEFAULT_PREALLOCS 8 #define BAST_CASTARD 1 /* Define to return NULL when casts fail */ #define NULL_PREP_VALUE ((gpointer)make_global_classfuncs) /* See camel_object_class_declare_event */ /* ** Quickie type system ************************************************* */ typedef struct _CamelTypeInfo { CamelType self; CamelType parent; const gchar *name; size_t instance_size; GMemChunk *instance_chunk; CamelObjectInitFunc instance_init; CamelObjectFinalizeFunc instance_finalize; GList *free_instances; size_t classfuncs_size; CamelObjectClassInitFunc class_init; CamelObjectClassFinalizeFunc class_finalize; CamelObjectClass *global_classfuncs; } CamelTypeInfo; typedef struct _CamelHookPair { CamelObjectEventHookFunc func; gpointer user_data; } CamelHookPair; /* ************************************************************************ */ static void camel_type_lock_up (void); static void camel_type_lock_down (void); static void obj_init (CamelObject * obj); static void obj_finalize (CamelObject * obj); static void obj_class_init (CamelObjectClass * class); static void obj_class_finalize (CamelObjectClass * class); static gboolean shared_is_of_type (CamelObjectShared * sh, CamelType ctype, gboolean is_obj); static void make_global_classfuncs (CamelTypeInfo * type_info); /* ************************************************************************ */ G_LOCK_DEFINE_STATIC (type_system); G_LOCK_DEFINE_STATIC (type_system_level); static GPrivate *type_system_locklevel = NULL; G_LOCK_DEFINE_STATIC (refcount); static gboolean type_system_initialized = FALSE; static GHashTable *ctype_to_typeinfo = NULL; static const CamelType camel_object_type = 1; static CamelType cur_max_type = CAMEL_INVALID_TYPE; /* ************************************************************************ */ #define LOCK_VAL (GPOINTER_TO_INT (g_private_get (type_system_locklevel))) #define LOCK_SET( val ) g_private_set (type_system_locklevel, GINT_TO_POINTER (val)) static void camel_type_lock_up (void) { G_LOCK (type_system_level); if (type_system_locklevel == NULL) type_system_locklevel = g_private_new (GINT_TO_POINTER (0)); if (LOCK_VAL == 0) { G_UNLOCK (type_system_level); G_LOCK (type_system); G_LOCK (type_system_level); } LOCK_SET (LOCK_VAL + 1); G_UNLOCK (type_system_level); } static void camel_type_lock_down (void) { G_LOCK (type_system_level); if (type_system_locklevel == NULL) { g_warning ("camel_type_lock_down: lock down before a lock up?"); type_system_locklevel = g_private_new (GINT_TO_POINTER (0)); G_UNLOCK (type_system_level); return; } LOCK_SET (LOCK_VAL - 1); if (LOCK_VAL == 0) G_UNLOCK (type_system); G_UNLOCK (type_system_level); } void camel_type_init (void) { CamelTypeInfo *obj_info; camel_type_lock_up (); if (type_system_initialized) { g_warning ("camel_type_init: type system already initialized."); camel_type_lock_down (); return; } type_system_initialized = TRUE; ctype_to_typeinfo = g_hash_table_new (g_direct_hash, g_direct_equal); obj_info = g_new (CamelTypeInfo, 1); obj_info->self = camel_object_type; obj_info->parent = CAMEL_INVALID_TYPE; obj_info->name = "CamelObject"; obj_info->instance_size = sizeof (CamelObject); obj_info->instance_chunk = g_mem_chunk_create (CamelObject, DEFAULT_PREALLOCS, G_ALLOC_ONLY); obj_info->instance_init = obj_init; obj_info->instance_finalize = obj_finalize; obj_info->free_instances = NULL; obj_info->classfuncs_size = sizeof (CamelObjectClass); obj_info->class_init = obj_class_init; obj_info->class_finalize = obj_class_finalize; g_hash_table_insert (ctype_to_typeinfo, GINT_TO_POINTER (CAMEL_INVALID_TYPE), NULL); g_hash_table_insert (ctype_to_typeinfo, GINT_TO_POINTER (camel_object_type), obj_info); /* Sigh. Ugly */ make_global_classfuncs (obj_info); cur_max_type = camel_object_type; camel_type_lock_down (); } CamelType camel_type_register (CamelType parent, const gchar * name, size_t instance_size, size_t classfuncs_size, CamelObjectClassInitFunc class_init, CamelObjectClassFinalizeFunc class_finalize, CamelObjectInitFunc instance_init, CamelObjectFinalizeFunc instance_finalize) { CamelTypeInfo *parent_info; CamelTypeInfo *obj_info; gchar *chunkname; g_return_val_if_fail (parent != CAMEL_INVALID_TYPE, CAMEL_INVALID_TYPE); g_return_val_if_fail (name, CAMEL_INVALID_TYPE); g_return_val_if_fail (instance_size, CAMEL_INVALID_TYPE); g_return_val_if_fail (classfuncs_size, CAMEL_INVALID_TYPE); camel_type_lock_up (); if (type_system_initialized == FALSE) { G_UNLOCK (type_system); camel_type_init (); G_LOCK (type_system); } parent_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (parent)); if (parent_info == NULL) { g_warning ("camel_type_register: no such parent type %d of class `%s'", parent, name); camel_type_lock_down (); return CAMEL_INVALID_TYPE; } if (parent_info->instance_size > instance_size) { g_warning ("camel_type_register: instance of class `%s' would be smaller than parent `%s'", name, parent_info->name); camel_type_lock_down (); return CAMEL_INVALID_TYPE; } if (parent_info->classfuncs_size > classfuncs_size) { g_warning ("camel_type_register: classfuncs of class `%s' would be smaller than parent `%s'", name, parent_info->name); camel_type_lock_down (); return CAMEL_INVALID_TYPE; } cur_max_type++; obj_info = g_new (CamelTypeInfo, 1); obj_info->self = cur_max_type; obj_info->parent = parent; obj_info->name = name; obj_info->instance_size = instance_size; chunkname = g_strdup_printf ("chunk for instances of Camel type `%s'", name); obj_info->instance_chunk = g_mem_chunk_new (chunkname, instance_size, instance_size * DEFAULT_PREALLOCS, G_ALLOC_ONLY); g_free (chunkname); obj_info->instance_init = instance_init; obj_info->instance_finalize = instance_finalize; obj_info->free_instances = NULL; obj_info->classfuncs_size = classfuncs_size; obj_info->class_init = class_init; obj_info->class_finalize = class_finalize; g_hash_table_insert (ctype_to_typeinfo, GINT_TO_POINTER (obj_info->self), obj_info); /* Sigh. Ugly. */ make_global_classfuncs (obj_info); camel_type_lock_down (); return obj_info->self; } CamelObjectClass * camel_type_get_global_classfuncs (CamelType type) { CamelTypeInfo *type_info; g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL); camel_type_lock_up (); type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (type)); camel_type_lock_down (); g_return_val_if_fail (type_info != NULL, NULL); return type_info->global_classfuncs; } const gchar * camel_type_to_name (CamelType type) { CamelTypeInfo *type_info; g_return_val_if_fail (type != CAMEL_INVALID_TYPE, "(the invalid type)"); camel_type_lock_up (); type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (type)); camel_type_lock_down (); g_return_val_if_fail (type_info != NULL, "(a bad type parameter was specified)"); return type_info->name; } /* ** The CamelObject ***************************************************** */ static void obj_init (CamelObject * obj) { obj->s.magic = CAMEL_OBJECT_MAGIC_VALUE; obj->ref_count = 1; obj->event_to_hooklist = NULL; obj->in_event = 0; } static void obj_finalize (CamelObject * obj) { g_return_if_fail (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE); g_return_if_fail (obj->ref_count == 0); g_return_if_fail (obj->in_event == 0); obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE; if (obj->event_to_hooklist) { #if 0 g_hash_table_foreach (obj->event_to_hooklist, (GHFunc) g_free, NULL); #endif g_hash_table_destroy (obj->event_to_hooklist); obj->event_to_hooklist = NULL; } } static void obj_class_init (CamelObjectClass * class) { class->s.magic = CAMEL_OBJECT_CLASS_MAGIC_VALUE; camel_object_class_declare_event (class, "finalize", NULL); } static void obj_class_finalize (CamelObjectClass * class) { g_return_if_fail (class->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE); class->s.magic = CAMEL_OBJECT_CLASS_FINALIZED_VALUE; if (class->event_to_preplist) { g_hash_table_foreach (class->event_to_preplist, (GHFunc) g_free, NULL); g_hash_table_destroy (class->event_to_preplist); class->event_to_preplist = NULL; } } CamelType camel_object_get_type (void) { if (type_system_initialized == FALSE) camel_type_init (); return camel_object_type; } CamelObject * camel_object_new (CamelType type) { CamelTypeInfo *type_info; GSList *parents = NULL; GSList *head = NULL; CamelObject *instance; g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL); /* Look up the type */ camel_type_lock_up (); type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (type)); if (type_info == NULL) { g_warning ("camel_object_new: trying to create object of invalid type %d", type); camel_type_lock_down (); return NULL; } /* Grab an instance out of the freed ones if possible, alloc otherwise */ if (type_info->free_instances) { GList *first; first = g_list_first (type_info->free_instances); instance = first->data; type_info->free_instances = g_list_remove_link (type_info->free_instances, first); g_list_free_1 (first); } else { instance = g_mem_chunk_alloc0 (type_info->instance_chunk); } /* Init the instance and classfuncs a bit */ instance->s.type = type; instance->classfuncs = type_info->global_classfuncs; /* Loop through the parents in simplest -> most complex order, initing the class and instance. * When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL * because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh? */ while (type_info) { parents = g_slist_prepend (parents, type_info); type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (type_info-> parent)); } head = parents; for (; parents && parents->data; parents = parents->next) { CamelTypeInfo *thisinfo; thisinfo = parents->data; if (thisinfo->instance_init) (thisinfo->instance_init) (instance); } g_slist_free (head); camel_type_lock_down (); return instance; } void camel_object_ref (CamelObject * obj) { g_return_if_fail (CAMEL_IS_OBJECT (obj)); G_LOCK (refcount); obj->ref_count++; G_UNLOCK (refcount); } void camel_object_unref (CamelObject * obj) { CamelTypeInfo *type_info; CamelTypeInfo *iter; GSList *parents = NULL; GSList *head = NULL; g_return_if_fail (CAMEL_IS_OBJECT (obj)); G_LOCK (refcount); obj->ref_count--; if (obj->ref_count > 0) { G_UNLOCK (refcount); return; } G_UNLOCK (refcount); /* Oh no! We want to emit a "finalized" event, but that function refs the object * because it's not supposed to get finalized in an event, but it is being finalized * right now, and AAUGH AAUGH AUGH AUGH! * * So we don't call camel_object_trigger_event. We do it ourselves. We even know * that CamelObject doesn't provide a prep for the finalized event, so we plunge * right in and call our hooks. * * And there was much rejoicing. */ #define hooklist parents /*cough */ if (obj->event_to_hooklist) { CamelHookPair *pair; hooklist = g_hash_table_lookup (obj->event_to_hooklist, "finalize"); while (hooklist && hooklist->data) { pair = hooklist->data; (pair->func) (obj, NULL, pair->user_data); hooklist = hooklist->next; } } hooklist = NULL; /* Don't mess with this line */ #undef hooklist /* Destroy it! hahaha! */ camel_type_lock_up (); type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (obj->s.type)); if (type_info == NULL) { g_warning ("camel_object_unref: seemingly valid object has a bad type %d", obj->s.type); camel_type_lock_down (); return; } /* Loop through the parents in most complex -> simplest order, finalizing the class * and instance. * * When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL * because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh? * * Use iter to preserve type_info for free_{instance,classfunc}s */ iter = type_info; while (iter) { parents = g_slist_prepend (parents, iter); iter = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (iter->parent)); } parents = g_slist_reverse (parents); head = parents; for (; parents && parents->data; parents = parents->next) { CamelTypeInfo *thisinfo; thisinfo = parents->data; if (thisinfo->instance_finalize) (thisinfo->instance_finalize) (obj); } g_slist_free (head); /* A little bit of cleaning up. * Don't erase the type, so we can peek at it if a finalized object * is check_cast'ed somewhere. */ memset (obj, 0, type_info->instance_size); obj->s.type = type_info->self; obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE; /* Tuck away the pointer for use in a new object */ type_info->free_instances = g_list_prepend (type_info->free_instances, obj); camel_type_lock_down (); } gboolean camel_object_is_of_type (CamelObject * obj, CamelType ctype) { return shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE); } gboolean camel_object_class_is_of_type (CamelObjectClass * class, CamelType ctype) { return shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE); } #ifdef BAST_CASTARD #define ERRVAL NULL #else #define ERRVAL obj #endif CamelObject * camel_object_check_cast (CamelObject * obj, CamelType ctype) { if (shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE)) return obj; return ERRVAL; } CamelObjectClass * camel_object_class_check_cast (CamelObjectClass * class, CamelType ctype) { if (shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE)) return class; return ERRVAL; } #undef ERRVAL gchar * camel_object_describe (CamelObject * obj) { if (obj == NULL) return g_strdup ("a NULL pointer"); if (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE) { return g_strdup_printf ("an instance of `%s' at %p", camel_type_to_name (obj->s.type), obj); } else if (obj->s.magic == CAMEL_OBJECT_FINALIZED_VALUE) { return g_strdup_printf ("a finalized instance of `%s' at %p", camel_type_to_name (obj->s.type), obj); } else if (obj->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) { return g_strdup_printf ("the classfuncs of `%s' at %p", camel_type_to_name (obj->s.type), obj); } else if (obj->s.magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) { return g_strdup_printf ("the finalized classfuncs of `%s' at %p", camel_type_to_name (obj->s.type), obj); } return g_strdup ("not a CamelObject"); } /* This is likely to be called in the class_init callback, * and the type will likely be somewhat uninitialized. * Is this a problem? We'll see.... */ void camel_object_class_declare_event (CamelObjectClass * class, const gchar * name, CamelObjectEventPrepFunc prep) { g_return_if_fail (CAMEL_IS_OBJECT_CLASS (class)); g_return_if_fail (name); if (class->event_to_preplist == NULL) class->event_to_preplist = g_hash_table_new (g_str_hash, g_str_equal); else if (g_hash_table_lookup (class->event_to_preplist, name) != NULL) { g_warning ("camel_object_class_declare_event: event `%s' already declared for `%s'", name, camel_type_to_name (class->s.type)); return; } /* AIEEEEEEEEEEEEEEEEEEEEEE * I feel so naughty. Since it's valid to declare an event and not * provide a hook, it should be valid to insert a NULL value into * the table. However, then our lookup in trigger_event would be * ambiguous, not telling us whether the event is undefined or whether * it merely has no hook. * * So we create an 'NULL prep' value that != NULL... specifically, it * equals the address of one of our static functions , because that * can't possibly be your hook. * * Just don't forget to check for the 'evil value' and it'll work, * I promise. */ if (prep == NULL) prep = NULL_PREP_VALUE; g_hash_table_insert (class->event_to_preplist, g_strdup (name), prep); } void camel_object_hook_event (CamelObject * obj, const gchar * name, CamelObjectEventHookFunc hook, gpointer user_data) { GSList *hooklist; CamelHookPair *pair; gpointer old_name, old_hooklist; g_return_if_fail (CAMEL_IS_OBJECT (obj)); g_return_if_fail (name); g_return_if_fail (hook); if (obj->event_to_hooklist == NULL) obj->event_to_hooklist = g_hash_table_new (g_str_hash, g_str_equal); pair = g_new (CamelHookPair, 1); pair->func = hook; pair->user_data = user_data; if (g_hash_table_lookup_extended (obj->event_to_hooklist, name, &old_name, &old_hooklist)) { hooklist = g_slist_prepend (old_hooklist, pair); g_hash_table_insert (obj->event_to_hooklist, old_name, hooklist); } else { hooklist = g_slist_prepend (NULL, pair); g_hash_table_insert (obj->event_to_hooklist, g_strdup (name), hooklist); } } void camel_object_unhook_event (CamelObject * obj, const gchar * name, CamelObjectEventHookFunc hook, gpointer user_data) { GSList *hooklist; GSList *head; g_return_if_fail (CAMEL_IS_OBJECT (obj)); g_return_if_fail (name); g_return_if_fail (hook); if (obj->event_to_hooklist == NULL) { g_warning ("camel_object_unhook_event: trying to unhook `%s' from an instance " "of `%s' with no hooks attached", name, camel_type_to_name (obj->s.type)); return; } hooklist = g_hash_table_lookup (obj->event_to_hooklist, name); if (hooklist == NULL) { g_warning ("camel_object_unhook_event: trying to unhook `%s' from an instance " "of `%s' with no hooks attached to that event.", name, camel_type_to_name (obj->s.type)); return; } head = hooklist; while (hooklist) { CamelHookPair *pair = (CamelHookPair *) hooklist->data; if (pair->func == hook && pair->user_data == user_data) { g_free (hooklist->data); head = g_slist_remove_link (head, hooklist); g_slist_free_1 (hooklist); g_hash_table_insert (obj->event_to_hooklist, (char *) name, head); return; } hooklist = hooklist->next; } g_warning ("camel_object_unhook_event: cannot find hook/data pair %p/%p in an " "instance of `%s' attached to `%s'", hook, user_data, camel_type_to_name (obj->s.type), name); } void camel_object_trigger_event (CamelObject * obj, const gchar * name, gpointer event_data) { GSList *hooklist; CamelHookPair *pair; CamelObjectEventPrepFunc prep; g_return_if_fail (CAMEL_IS_OBJECT (obj)); g_return_if_fail (name); if (obj->in_event) { g_warning ("camel_object_trigger_event: trying to trigger `%s' in class " "`%s' while already triggering another event", name, camel_type_to_name (obj->s.type)); return; } if (obj->classfuncs->event_to_preplist == NULL) { g_warning ("camel_object_trigger_event: trying to trigger `%s' in class " "`%s' with no defined events.", name, camel_type_to_name (obj->s.type)); return; } prep = g_hash_table_lookup (obj->classfuncs->event_to_preplist, name); if (prep == NULL) { g_warning ("camel_object_trigger_event: trying to trigger undefined " "event `%s' in class `%s'.", name, camel_type_to_name (obj->s.type)); return; } /* Ref so that it can't get destroyed in the event, which would * be Bad. And it's a valid ref anyway... */ camel_object_ref (obj); obj->in_event = 1; if ((prep != NULL_PREP_VALUE && !prep (obj, event_data)) || obj->event_to_hooklist == NULL) { obj->in_event = 0; camel_object_unref (obj); return; } hooklist = g_hash_table_lookup (obj->event_to_hooklist, name); while (hooklist && hooklist->data) { pair = hooklist->data; (pair->func) (obj, event_data, pair->user_data); hooklist = hooklist->next; } obj->in_event = 0; camel_object_unref (obj); } /* ** Static helpers ****************************************************** */ static gboolean shared_is_of_type (CamelObjectShared * sh, CamelType ctype, gboolean is_obj) { CamelTypeInfo *type_info; gchar *targtype; if (is_obj) targtype = "instance"; else targtype = "classdata"; if (ctype == CAMEL_INVALID_TYPE) { g_warning ("shared_is_of_type: trying to cast to CAMEL_INVALID_TYPE"); return FALSE; } if (sh == NULL) { g_warning ("shared_is_of_type: trying to cast NULL to %s of `%s'", targtype, camel_type_to_name (ctype)); return FALSE; } if (sh->magic == CAMEL_OBJECT_FINALIZED_VALUE) { g_warning ("shared_is_of_type: trying to cast finalized instance " "of `%s' into %s of `%s'", camel_type_to_name (sh->type), targtype, camel_type_to_name (ctype)); return FALSE; } if (sh->magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) { g_warning ("shared_is_of_type: trying to cast finalized classdata " "of `%s' into %s of `%s'", camel_type_to_name (sh->type), targtype, camel_type_to_name (ctype)); return FALSE; } if (is_obj) { if (sh->magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) { g_warning ("shared_is_of_type: trying to cast classdata " "of `%s' into instance of `%s'", camel_type_to_name (sh->type), camel_type_to_name (ctype)); return FALSE; } if (sh->magic != CAMEL_OBJECT_MAGIC_VALUE) { g_warning ("shared_is_of_type: trying to cast junk data " "into instance of `%s'", camel_type_to_name (ctype)); return FALSE; } } else { if (sh->magic == CAMEL_OBJECT_MAGIC_VALUE) { g_warning ("shared_is_of_type: trying to cast instance " "of `%s' into classdata of `%s'", camel_type_to_name (sh->type), camel_type_to_name (ctype)); return FALSE; } if (sh->magic != CAMEL_OBJECT_CLASS_MAGIC_VALUE) { g_warning ("shared_is_of_type: trying to cast junk data " "into classdata of `%s'", camel_type_to_name (ctype)); return FALSE; } } camel_type_lock_up (); type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (sh->type)); if (type_info == NULL) { g_warning ("shared_is_of_type: seemingly valid %s has " "bad type %d.", targtype, sh->type); camel_type_lock_down (); return FALSE; } while (type_info) { if (type_info->self == ctype) { camel_type_lock_down (); return TRUE; } type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (type_info-> parent)); } /* this isn't an error, e.g. CAMEL_IS_FOLDER(folder), its upto the caller to handle the false case */ /*g_warning ("shared_is_of_type: %s of `%s' (@%p) is not also %s of `%s'", targtype, camel_type_to_name (sh->type), sh, targtype, camel_type_to_name (ctype));*/ camel_type_lock_down (); return FALSE; } static void make_global_classfuncs (CamelTypeInfo * type_info) { CamelObjectClass *funcs; GSList *parents; GSList *head; g_assert (type_info); funcs = g_malloc0 (type_info->classfuncs_size); funcs->s.type = type_info->self; type_info->global_classfuncs = funcs; parents = NULL; while (type_info) { parents = g_slist_prepend (parents, type_info); type_info = g_hash_table_lookup (ctype_to_typeinfo, GINT_TO_POINTER (type_info-> parent)); } head = parents; for (; parents && parents->data; parents = parents->next) { CamelTypeInfo *thisinfo; thisinfo = parents->data; if (thisinfo->class_init) (thisinfo->class_init) (funcs); } g_slist_free (head); }