Wed, 23 Feb 2005 19:05:01 +0100
Added dlist iteration macros.
| 60 | 1 | /* |
| 2 | * libtu/obj.c | |
| 3 | * | |
| 4 | * Copyright (c) Tuomo Valkonen 1999-2004. | |
| 5 | * | |
| 6 | * You may distribute and modify this library under the terms of either | |
| 7 | * the Clarified Artistic License or the GNU LGPL, version 2.1 or later. | |
| 8 | */ | |
| 9 | ||
| 10 | #include <string.h> | |
| 11 | ||
| 12 | #include "types.h" | |
| 13 | #include "obj.h" | |
| 14 | #include "objp.h" | |
| 15 | #include "misc.h" | |
| 16 | #include "dlist.h" | |
| 17 | ||
| 18 | ||
| 19 | ClassDescr CLASSDESCR(Obj)={"Obj", NULL, 0, NULL, NULL}; | |
| 20 | ||
| 21 | ||
| 22 | static void do_watches(Obj *obj, bool call); | |
| 23 | ||
| 24 | ||
| 25 | /*{{{ Destroy */ | |
| 26 | ||
| 27 | ||
| 28 | void destroy_obj(Obj *obj) | |
| 29 | { | |
| 62 | 30 | ClassDescr *d; |
| 31 | ||
| 32 | if(OBJ_IS_BEING_DESTROYED(obj)) | |
| 33 | return; | |
| 34 | ||
| 35 | obj->flags|=OBJ_DEST; | |
| 36 | ||
| 37 | do_watches(obj, TRUE); | |
| 38 | ||
| 39 | d=obj->obj_type; | |
| 40 | ||
| 41 | while(d!=NULL){ | |
| 42 | if(d->destroy_fn!=NULL){ | |
| 43 | d->destroy_fn(obj); | |
| 44 | break; | |
| 45 | } | |
| 46 | d=d->ancestor; | |
| 47 | } | |
| 48 | ||
| 49 | do_watches(obj, FALSE); | |
| 60 | 50 | |
| 62 | 51 | free(obj); |
| 60 | 52 | } |
| 53 | ||
| 54 | ||
| 55 | /*}}}*/ | |
| 56 | ||
| 57 | ||
| 58 | /*{{{ is/cast */ | |
| 59 | ||
| 60 | ||
| 61 | bool obj_is(const Obj *obj, const ClassDescr *descr) | |
| 62 | { | |
| 62 | 63 | ClassDescr *d; |
| 64 | ||
| 65 | if(obj==NULL) | |
| 66 | return FALSE; | |
| 67 | ||
| 68 | d=obj->obj_type; | |
| 69 | ||
| 70 | while(d!=NULL){ | |
| 71 | if(d==descr) | |
| 72 | return TRUE; | |
| 73 | d=d->ancestor; | |
| 74 | } | |
| 75 | return FALSE; | |
| 60 | 76 | } |
| 77 | ||
| 78 | ||
| 79 | bool obj_is_str(const Obj *obj, const char *str) | |
| 80 | { | |
| 62 | 81 | ClassDescr *d; |
| 82 | ||
| 83 | if(obj==NULL || str==NULL) | |
| 84 | return FALSE; | |
| 85 | ||
| 86 | d=obj->obj_type; | |
| 87 | ||
| 88 | while(d!=NULL){ | |
| 89 | if(strcmp(d->name, str)==0) | |
| 90 | return TRUE; | |
| 91 | d=d->ancestor; | |
| 92 | } | |
| 93 | return FALSE; | |
| 60 | 94 | } |
| 95 | ||
| 96 | ||
| 97 | const void *obj_cast(const Obj *obj, const ClassDescr *descr) | |
| 98 | { | |
| 62 | 99 | ClassDescr *d; |
| 100 | ||
| 101 | if(obj==NULL) | |
| 102 | return NULL; | |
| 103 | ||
| 104 | d=obj->obj_type; | |
| 105 | ||
| 106 | while(d!=NULL){ | |
| 107 | if(d==descr) | |
| 108 | return (void*)obj; | |
| 109 | d=d->ancestor; | |
| 110 | } | |
| 111 | return NULL; | |
| 60 | 112 | } |
| 113 | ||
| 114 | ||
| 115 | /*}}}*/ | |
| 116 | ||
| 117 | ||
| 118 | /*{{{ Dynamic functions */ | |
| 119 | ||
| 120 | ||
| 121 | /* This function is called when no handler is found. | |
| 122 | */ | |
| 123 | static void dummy_dyn() | |
| 124 | { | |
| 125 | } | |
| 126 | ||
| 127 | ||
| 128 | static int comp_fun(const void *a, const void *b) | |
| 129 | { | |
| 62 | 130 | void *af=(void*)((DynFunTab*)a)->func; |
| 131 | void *bf=(void*)((DynFunTab*)b)->func; | |
| 132 | ||
| 133 | return (af<bf ? -1 : (af==bf ? 0 : 1)); | |
| 60 | 134 | } |
| 135 | ||
| 62 | 136 | |
| 60 | 137 | DynFun *lookup_dynfun(const Obj *obj, DynFun *func, |
| 62 | 138 | bool *funnotfound) |
| 60 | 139 | { |
| 62 | 140 | ClassDescr *descr; |
| 141 | DynFunTab *df; | |
| 142 | /*DynFunTab dummy={NULL, NULL};*/ | |
| 143 | /*dummy.func=func;*/ | |
| 144 | ||
| 145 | if(obj==NULL) | |
| 146 | return NULL; | |
| 147 | ||
| 148 | descr=obj->obj_type; | |
| 149 | ||
| 150 | for(; descr!=&Obj_classdescr; descr=descr->ancestor){ | |
| 151 | if(descr->funtab==NULL) | |
| 152 | continue; | |
| 153 | ||
| 154 | if(descr->funtab_n==-1){ | |
| 155 | /* Need to sort the table. */ | |
| 156 | descr->funtab_n=0; | |
| 157 | df=descr->funtab; | |
| 158 | while(df->func!=NULL){ | |
| 159 | descr->funtab_n++; | |
| 160 | df++; | |
| 161 | } | |
| 162 | ||
| 163 | if(descr->funtab_n>0){ | |
| 164 | qsort(descr->funtab, descr->funtab_n, sizeof(DynFunTab), | |
| 165 | comp_fun); | |
| 166 | } | |
| 167 | } | |
| 168 | ||
| 169 | /* | |
| 170 | if(descr->funtab_n==0) | |
| 171 | continue; | |
| 172 | ||
| 173 | df=(DynFunTab*)bsearch(&dummy, descr->funtab, descr->funtab_n, | |
| 174 | sizeof(DynFunTab), comp_fun); | |
| 175 | if(df!=NULL){ | |
| 176 | *funnotfound=FALSE; | |
| 177 | return df->handler; | |
| 178 | } | |
| 179 | */ | |
| 180 | ||
| 181 | /* Using custom bsearch instead of the one in libc is probably | |
| 182 | * faster as the overhead of calling a comparison function would | |
| 183 | * be significant given that the comparisons are simple and | |
| 184 | * funtab_n not that big. | |
| 185 | */ | |
| 186 | { | |
| 187 | int min=0, max=descr->funtab_n-1; | |
| 188 | int ndx; | |
| 189 | df=descr->funtab; | |
| 190 | while(max>=min){ | |
| 191 | ndx=(max+min)/2; | |
| 192 | if((void*)df[ndx].func==(void*)func){ | |
| 193 | *funnotfound=FALSE; | |
| 194 | return df[ndx].handler; | |
| 195 | } | |
| 196 | if((void*)df[ndx].func<(void*)func) | |
| 197 | min=ndx+1; | |
| 198 | else | |
| 199 | max=ndx-1; | |
| 200 | } | |
| 201 | } | |
| 202 | } | |
| 203 | ||
| 204 | *funnotfound=TRUE; | |
| 205 | return dummy_dyn; | |
| 60 | 206 | } |
| 207 | ||
| 208 | ||
| 209 | bool has_dynfun(const Obj *obj, DynFun *func) | |
| 210 | { | |
| 62 | 211 | bool funnotfound; |
| 212 | lookup_dynfun(obj, func, &funnotfound); | |
| 213 | return !funnotfound; | |
| 60 | 214 | } |
| 215 | ||
| 216 | ||
| 217 | /*}}}*/ | |
| 218 | ||
| 219 | ||
| 220 | /*{{{ Watches */ | |
| 221 | ||
| 222 | ||
| 223 | bool watch_setup(Watch *watch, Obj *obj, WatchHandler *handler) | |
| 224 | { | |
| 62 | 225 | if(OBJ_IS_BEING_DESTROYED(obj)) |
| 226 | return FALSE; | |
| 227 | ||
| 228 | watch_reset(watch); | |
| 229 | ||
| 230 | watch->handler=handler; | |
| 231 | LINK_ITEM(obj->obj_watches, watch, next, prev); | |
| 232 | watch->obj=obj; | |
| 233 | ||
| 234 | return TRUE; | |
| 60 | 235 | } |
| 236 | ||
| 237 | void do_watch_reset(Watch *watch, bool call) | |
| 238 | { | |
| 62 | 239 | WatchHandler *handler=watch->handler; |
| 240 | Obj *obj=watch->obj; | |
| 241 | ||
| 242 | watch->handler=NULL; | |
| 243 | ||
| 244 | if(obj==NULL) | |
| 245 | return; | |
| 246 | ||
| 247 | UNLINK_ITEM(obj->obj_watches, watch, next, prev); | |
| 248 | watch->obj=NULL; | |
| 249 | ||
| 250 | if(call && handler!=NULL) | |
| 251 | handler(watch, obj); | |
| 60 | 252 | } |
| 253 | ||
| 254 | ||
| 255 | void watch_reset(Watch *watch) | |
| 256 | { | |
| 62 | 257 | do_watch_reset(watch, FALSE); |
| 60 | 258 | } |
| 259 | ||
| 260 | ||
| 261 | bool watch_ok(Watch *watch) | |
| 262 | { | |
| 62 | 263 | return watch->obj!=NULL; |
| 60 | 264 | } |
| 265 | ||
| 266 | ||
| 267 | static void do_watches(Obj *obj, bool call) | |
| 268 | { | |
| 62 | 269 | Watch *watch, *next; |
| 60 | 270 | |
| 62 | 271 | watch=obj->obj_watches; |
| 272 | ||
| 273 | while(watch!=NULL){ | |
| 274 | next=watch->next; | |
| 275 | do_watch_reset(watch, call); | |
| 276 | watch=next; | |
| 277 | } | |
| 60 | 278 | } |
| 279 | ||
| 280 | ||
| 281 | void watch_call(Obj *obj) | |
| 282 | { | |
| 62 | 283 | do_watches(obj, FALSE); |
| 60 | 284 | } |
| 285 | ||
| 286 | ||
| 287 | void watch_init(Watch *watch) | |
| 288 | { | |
| 62 | 289 | watch->obj=NULL; |
| 290 | watch->next=NULL; | |
| 291 | watch->prev=NULL; | |
| 292 | watch->handler=NULL; | |
| 60 | 293 | } |
| 294 | ||
| 295 | ||
| 296 | /*}}}*/ | |
| 297 |