SUMO - Simulation of Urban MObility
debug_new.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
00002 // vim:tabstop=4:shiftwidth=4:expandtab:
00003 #ifdef _MSC_VER
00004 #include <windows_config.h>
00005 #else
00006 #include <config.h>
00007 #endif
00008 #ifdef CHECK_MEMORY_LEAKS
00009 /*
00010  * Copyright (C) 2004-2008 Wu Yongwei <adah at users dot sourceforge dot net>
00011  *
00012  * This software is provided 'as-is', without any express or implied
00013  * warranty.  In no event will the authors be held liable for any
00014  * damages arising from the use of this software.
00015  *
00016  * Permission is granted to anyone to use this software for any purpose,
00017  * including commercial applications, and to alter it and redistribute
00018  * it freely, subject to the following restrictions:
00019  *
00020  * 1. The origin of this software must not be misrepresented; you must
00021  *    not claim that you wrote the original software.  If you use this
00022  *    software in a product, an acknowledgement in the product
00023  *    documentation would be appreciated but is not required.
00024  * 2. Altered source versions must be plainly marked as such, and must
00025  *    not be misrepresented as being the original software.
00026  * 3. This notice may not be removed or altered from any source
00027  *    distribution.
00028  *
00029  * This file is part of Stones of Nvwa:
00030  *      http://sourceforge.net/projects/nvwa
00031  *
00032  */
00033 
00044 #include <new>
00045 #include <assert.h>
00046 #include <limits.h>
00047 #include <stdio.h>
00048 #include <stdlib.h>
00049 #include <string.h>
00050 #ifdef __unix__
00051 #include <alloca.h>
00052 #endif
00053 #ifdef _WIN32
00054 #include <malloc.h>
00055 #endif
00056 #include "fast_mutex.h"
00057 #include "static_assert.h"
00058 
00059 #if !_FAST_MUTEX_CHECK_INITIALIZATION && !defined(_NOTHREADS)
00060 #error "_FAST_MUTEX_CHECK_INITIALIZATION not set: check_leaks may not work"
00061 #endif
00062 
00069 #ifndef _DEBUG_NEW_ALIGNMENT
00070 #define _DEBUG_NEW_ALIGNMENT 16
00071 #endif
00072 
00080 #ifndef _DEBUG_NEW_CALLER_ADDRESS
00081 #ifdef __GNUC__
00082 #define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0)
00083 #else
00084 #define _DEBUG_NEW_CALLER_ADDRESS NULL
00085 #endif
00086 #endif
00087 
00097 #ifndef _DEBUG_NEW_ERROR_ACTION
00098 #ifndef _DEBUG_NEW_ERROR_CRASH
00099 #define _DEBUG_NEW_ERROR_ACTION abort()
00100 #else
00101 #define _DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0)
00102 #endif
00103 #endif
00104 
00116 #ifndef _DEBUG_NEW_FILENAME_LEN
00117 #define _DEBUG_NEW_FILENAME_LEN 80
00118 #endif
00119 
00132 #ifndef _DEBUG_NEW_PROGNAME
00133 #define _DEBUG_NEW_PROGNAME NULL
00134 #endif
00135 
00143 #ifndef _DEBUG_NEW_STD_OPER_NEW
00144 #define _DEBUG_NEW_STD_OPER_NEW 1
00145 #endif
00146 
00154 #ifndef _DEBUG_NEW_TAILCHECK
00155 #define _DEBUG_NEW_TAILCHECK 0
00156 #endif
00157 
00163 #ifndef _DEBUG_NEW_TAILCHECK_CHAR
00164 #define _DEBUG_NEW_TAILCHECK_CHAR 0xCC
00165 #endif
00166 
00175 #ifndef _DEBUG_NEW_USE_ADDR2LINE
00176 #ifdef __GNUC__
00177 #define _DEBUG_NEW_USE_ADDR2LINE 1
00178 #else
00179 #define _DEBUG_NEW_USE_ADDR2LINE 0
00180 #endif
00181 #endif
00182 
00183 #ifdef _MSC_VER
00184 #pragma warning(disable: 4073)  // #pragma init_seg(lib) used
00185 #pragma warning(disable: 4290)  // C++ exception specification ignored
00186 #pragma init_seg(lib)
00187 #endif
00188 
00189 #undef  _DEBUG_NEW_EMULATE_MALLOC
00190 #undef  _DEBUG_NEW_REDEFINE_NEW
00191 
00195 #define _DEBUG_NEW_REDEFINE_NEW 0
00196 #include "debug_new.h"
00197 
00201 #define align(s) \
00202         (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1))
00203 
00207 struct new_ptr_list_t
00208 {
00209     new_ptr_list_t*     next;
00210     new_ptr_list_t*     prev;
00211     size_t              size;
00212     union
00213     {
00214 #if _DEBUG_NEW_FILENAME_LEN == 0
00215     const char*         file;
00216 #else
00217     char                file[_DEBUG_NEW_FILENAME_LEN];
00218 #endif
00219     void*               addr;
00220     };
00221     unsigned            line      :31;
00222     unsigned            is_array  :1;
00223     unsigned            magic;
00224 };
00225 
00229 const unsigned MAGIC = 0x4442474E;
00230 
00234 const int ALIGNED_LIST_ITEM_SIZE = align(sizeof(new_ptr_list_t));
00235 
00239 static new_ptr_list_t new_ptr_list = {
00240     &new_ptr_list,
00241     &new_ptr_list,
00242     0,
00243     {
00244 #if _DEBUG_NEW_FILENAME_LEN == 0
00245         NULL
00246 #else
00247         ""
00248 #endif
00249     },
00250     0,
00251     0,
00252     MAGIC
00253 };
00254 
00258 static fast_mutex new_ptr_lock;
00259 
00263 static fast_mutex new_output_lock;
00264 
00268 static size_t total_mem_alloc = 0;
00269 
00274 bool new_autocheck_flag = true;
00275 
00279 bool new_verbose_flag = false;
00280 
00286 FILE* new_output_fp = stderr;
00287 
00296 const char* new_progname = _DEBUG_NEW_PROGNAME;
00297 
00298 #if _DEBUG_NEW_USE_ADDR2LINE
00299 
00308 static bool print_position_from_addr(const void* addr)
00309 {
00310     static const void* last_addr = NULL;
00311     static char last_info[256] = "";
00312     if (addr == last_addr)
00313     {
00314         if (last_info[0] == '\0')
00315             return false;
00316         fprintf(new_output_fp, "%s", last_info);
00317         return true;
00318     }
00319     if (new_progname)
00320     {
00321         const char addr2line_cmd[] = "addr2line -e ";
00322 #if   defined(__CYGWIN__) || defined(_WIN32)
00323         const int  exeext_len = 4;
00324 #else
00325         const int  exeext_len = 0;
00326 #endif
00327 #if  !defined(__CYGWIN__) && defined(__unix__)
00328         const char ignore_err[] = " 2>/dev/null";
00329 #elif defined(__CYGWIN__) || \
00330         (defined(_WIN32) && defined(WINVER) && WINVER >= 0x0500)
00331         const char ignore_err[] = " 2>nul";
00332 #else
00333         const char ignore_err[] = "";
00334 #endif
00335         char* cmd = (char*)alloca(strlen(new_progname)
00336                                   + exeext_len
00337                                   + sizeof addr2line_cmd - 1
00338                                   + sizeof ignore_err - 1
00339                                   + sizeof(void*) * 2
00340                                   + 4 /* SP + "0x" + null */);
00341         strcpy(cmd, addr2line_cmd);
00342         strcpy(cmd + sizeof addr2line_cmd - 1, new_progname);
00343         size_t len = strlen(cmd);
00344 #if   defined(__CYGWIN__) || defined(_WIN32)
00345         if (len <= 4
00346                 || (strcmp(cmd + len - 4, ".exe") != 0 &&
00347                     strcmp(cmd + len - 4, ".EXE") != 0))
00348         {
00349             strcpy(cmd + len, ".exe");
00350             len += 4;
00351         }
00352 #endif
00353         sprintf(cmd + len, " %p%s", addr, ignore_err);
00354         FILE* fp = popen(cmd, "r");
00355         if (fp)
00356         {
00357             char buffer[sizeof last_info] = "";
00358             len = 0;
00359             if (fgets(buffer, sizeof buffer, fp))
00360             {
00361                 len = strlen(buffer);
00362                 if (buffer[len - 1] == '\n')
00363                     buffer[--len] = '\0';
00364             }
00365             int res = pclose(fp);
00366             // Display the file/line information only if the command
00367             // is executed successfully and the output points to a
00368             // valid position, but the result will be cached if only
00369             // the command is executed successfully.
00370             if (res == 0 && len > 0)
00371             {
00372                 last_addr = addr;
00373                 if (buffer[len - 1] == '0' && buffer[len - 2] == ':')
00374                     last_info[0] = '\0';
00375                 else
00376                 {
00377                     fprintf(new_output_fp, "%s", buffer);
00378                     strcpy(last_info, buffer);
00379                     return true;
00380                 }
00381             }
00382         }
00383     }
00384     return false;
00385 }
00386 #else
00387 
00393 static bool print_position_from_addr(const void*)
00394 {
00395     return false;
00396 }
00397 #endif // _DEBUG_NEW_USE_ADDR2LINE
00398 
00410 static void print_position(const void* ptr, int line)
00411 {
00412     if (line != 0)          // Is file/line information present?
00413     {
00414         fprintf(new_output_fp, "%s:%d", (const char*)ptr, line);
00415     }
00416     else if (ptr != NULL)   // Is caller address present?
00417     {
00418         if (!print_position_from_addr(ptr)) // Fail to get source position?
00419             fprintf(new_output_fp, "%p", ptr);
00420     }
00421     else                    // No information is present
00422     {
00423         fprintf(new_output_fp, "<Unknown>");
00424     }
00425 }
00426 
00427 #if _DEBUG_NEW_TAILCHECK
00428 
00436 static bool check_tail(new_ptr_list_t* ptr)
00437 {
00438     const unsigned char* const pointer = (unsigned char*)ptr +
00439                             ALIGNED_LIST_ITEM_SIZE + ptr->size;
00440     for (int i = 0; i < _DEBUG_NEW_TAILCHECK; ++i)
00441         if (pointer[i] != _DEBUG_NEW_TAILCHECK_CHAR)
00442             return false;
00443     return true;
00444 }
00445 #endif
00446 
00457 static void* alloc_mem(size_t size, const char* file, int line, bool is_array)
00458 {
00459     assert(line >= 0);
00460     STATIC_ASSERT((_DEBUG_NEW_ALIGNMENT & (_DEBUG_NEW_ALIGNMENT - 1)) == 0,
00461                   Alignment_must_be_power_of_two);
00462     STATIC_ASSERT(_DEBUG_NEW_TAILCHECK >= 0, Invalid_tail_check_length);
00463     size_t s = size + ALIGNED_LIST_ITEM_SIZE + _DEBUG_NEW_TAILCHECK;
00464     new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s);
00465     if (ptr == NULL)
00466     {
00467 #if _DEBUG_NEW_STD_OPER_NEW
00468         return NULL;
00469 #else
00470         fast_mutex_autolock lock(new_output_lock);
00471         fprintf(new_output_fp,
00472                 "Out of memory when allocating %u bytes\n",
00473                 size);
00474         fflush(new_output_fp);
00475         _DEBUG_NEW_ERROR_ACTION;
00476 #endif
00477     }
00478     void* pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
00479 #if _DEBUG_NEW_FILENAME_LEN == 0
00480     ptr->file = file;
00481 #else
00482     if (line)
00483         strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1)
00484                 [_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
00485     else
00486         ptr->addr = (void*)file;
00487 #endif
00488     ptr->line = line;
00489     ptr->is_array = is_array;
00490     ptr->size = size;
00491     ptr->magic = MAGIC;
00492     {
00493         fast_mutex_autolock lock(new_ptr_lock);
00494         ptr->prev = new_ptr_list.prev;
00495         ptr->next = &new_ptr_list;
00496         new_ptr_list.prev->next = ptr;
00497         new_ptr_list.prev = ptr;
00498     }
00499 #if _DEBUG_NEW_TAILCHECK
00500     memset((char*)pointer + size, _DEBUG_NEW_TAILCHECK_CHAR,
00501                                   _DEBUG_NEW_TAILCHECK);
00502 #endif
00503     if (new_verbose_flag)
00504     {
00505         fast_mutex_autolock lock(new_output_lock);
00506         fprintf(new_output_fp,
00507                 "new%s: allocated %p (size %u, ",
00508                 is_array ? "[]" : "",
00509                 pointer, size);
00510         if (line != 0)
00511             print_position(ptr->file, ptr->line);
00512         else
00513             print_position(ptr->addr, ptr->line);
00514         fprintf(new_output_fp, ")\n");
00515     }
00516     total_mem_alloc += size;
00517     return pointer;
00518 }
00519 
00528 static void free_pointer(void* pointer, void* addr, bool is_array)
00529 {
00530     if (pointer == NULL)
00531         return;
00532     new_ptr_list_t* ptr =
00533             (new_ptr_list_t*)((char*)pointer - ALIGNED_LIST_ITEM_SIZE);
00534     if (ptr->magic != MAGIC)
00535     {
00536         {
00537             fast_mutex_autolock lock(new_output_lock);
00538             fprintf(new_output_fp, "delete%s: invalid pointer %p (",
00539                     is_array ? "[]" : "", pointer);
00540             print_position(addr, 0);
00541             fprintf(new_output_fp, ")\n");
00542         }
00543         check_mem_corruption();
00544         fflush(new_output_fp);
00545         _DEBUG_NEW_ERROR_ACTION;
00546     }
00547     if (is_array != ptr->is_array)
00548     {
00549         const char* msg;
00550         if (is_array)
00551             msg = "delete[] after new";
00552         else
00553             msg = "delete after new[]";
00554         fast_mutex_autolock lock(new_output_lock);
00555         fprintf(new_output_fp,
00556                 "%s: pointer %p (size %u)\n\tat ",
00557                 msg,
00558                 (char*)ptr + ALIGNED_LIST_ITEM_SIZE,
00559                 ptr->size);
00560         print_position(addr, 0);
00561         fprintf(new_output_fp, "\n\toriginally allocated at ");
00562         if (ptr->line != 0)
00563             print_position(ptr->file, ptr->line);
00564         else
00565             print_position(ptr->addr, ptr->line);
00566         fprintf(new_output_fp, "\n");
00567         fflush(new_output_fp);
00568         _DEBUG_NEW_ERROR_ACTION;
00569     }
00570 #if _DEBUG_NEW_TAILCHECK
00571     if (!check_tail(ptr))
00572     {
00573         check_mem_corruption();
00574         fflush(new_output_fp);
00575         _DEBUG_NEW_ERROR_ACTION;
00576     }
00577 #endif
00578     {
00579         fast_mutex_autolock lock(new_ptr_lock);
00580         total_mem_alloc -= ptr->size;
00581         ptr->magic = 0;
00582         ptr->prev->next = ptr->next;
00583         ptr->next->prev = ptr->prev;
00584     }
00585     if (new_verbose_flag)
00586     {
00587         fast_mutex_autolock lock(new_output_lock);
00588         fprintf(new_output_fp,
00589                 "delete%s: freed %p (size %u, %u bytes still allocated)\n",
00590                 is_array ? "[]" : "",
00591                 (char*)ptr + ALIGNED_LIST_ITEM_SIZE,
00592                 ptr->size, total_mem_alloc);
00593     }
00594     free(ptr);
00595     return;
00596 }
00597 
00603 int check_leaks()
00604 {
00605     int leak_cnt = 0;
00606     fast_mutex_autolock lock_ptr(new_ptr_lock);
00607     fast_mutex_autolock lock_output(new_output_lock);
00608     new_ptr_list_t* ptr = new_ptr_list.next;
00609     while (ptr != &new_ptr_list)
00610     {
00611         const char* const pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
00612         if (ptr->magic != MAGIC)
00613         {
00614             fprintf(new_output_fp,
00615                     "warning: heap data corrupt near %p\n",
00616                     pointer);
00617         }
00618 #if _DEBUG_NEW_TAILCHECK
00619         if (!check_tail(ptr))
00620         {
00621             fprintf(new_output_fp,
00622                     "warning: overwritten past end of object at %p\n",
00623                     pointer);
00624         }
00625 #endif
00626         fprintf(new_output_fp,
00627                 "Leaked object at %p (size %u, ",
00628                 pointer,
00629                 ptr->size);
00630         if (ptr->line != 0)
00631             print_position(ptr->file, ptr->line);
00632         else
00633             print_position(ptr->addr, ptr->line);
00634         fprintf(new_output_fp, ")\n");
00635         ptr = ptr->next;
00636         ++leak_cnt;
00637     }
00638     if (new_verbose_flag || leak_cnt)
00639         fprintf(new_output_fp, "*** %d leaks found\n", leak_cnt);
00640     return leak_cnt;
00641 }
00642 
00649 int check_mem_corruption()
00650 {
00651     int corrupt_cnt = 0;
00652     fast_mutex_autolock lock_ptr(new_ptr_lock);
00653     fast_mutex_autolock lock_output(new_output_lock);
00654     fprintf(new_output_fp, "*** Checking for memory corruption: START\n");
00655     for (new_ptr_list_t* ptr = new_ptr_list.next;
00656             ptr != &new_ptr_list;
00657             ptr = ptr->next)
00658     {
00659         const char* const pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
00660         if (ptr->magic == MAGIC
00661 #if _DEBUG_NEW_TAILCHECK
00662                 && check_tail(ptr)
00663 #endif
00664                 )
00665             continue;
00666 #if _DEBUG_NEW_TAILCHECK
00667         if (ptr->magic != MAGIC)
00668         {
00669 #endif
00670             fprintf(new_output_fp,
00671                     "Heap data corrupt near %p (size %u, ",
00672                     pointer,
00673                     ptr->size);
00674 #if _DEBUG_NEW_TAILCHECK
00675         }
00676         else
00677         {
00678             fprintf(new_output_fp,
00679                     "Overwritten past end of object at %p (size %u, ",
00680                     pointer,
00681                     ptr->size);
00682         }
00683 #endif
00684         if (ptr->line != 0)
00685             print_position(ptr->file, ptr->line);
00686         else
00687             print_position(ptr->addr, ptr->line);
00688         fprintf(new_output_fp, ")\n");
00689         ++corrupt_cnt;
00690     }
00691     fprintf(new_output_fp, "*** Checking for memory corruption: %d FOUND\n",
00692             corrupt_cnt);
00693     return corrupt_cnt;
00694 }
00695 
00696 void __debug_new_recorder::_M_process(void* pointer)
00697 {
00698     if (pointer == NULL)
00699         return;
00700     new_ptr_list_t* ptr =
00701             (new_ptr_list_t*)((char*)pointer - ALIGNED_LIST_ITEM_SIZE);
00702     if (ptr->magic != MAGIC || ptr->line != 0)
00703     {
00704         fast_mutex_autolock lock(new_output_lock);
00705         fprintf(new_output_fp,
00706                 "warning: debug_new used with placement new (%s:%d)\n",
00707                 _M_file, _M_line);
00708         return;
00709     }
00710 #if _DEBUG_NEW_FILENAME_LEN == 0
00711     ptr->file = _M_file;
00712 #else
00713     strncpy(ptr->file, _M_file, _DEBUG_NEW_FILENAME_LEN - 1)
00714             [_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
00715 #endif
00716     ptr->line = _M_line;
00717 }
00718 
00719 void* operator new(size_t size, const char* file, int line)
00720 {
00721     void* ptr = alloc_mem(size, file, line, false);
00722 #if _DEBUG_NEW_STD_OPER_NEW
00723     if (ptr)
00724         return ptr;
00725     else
00726         throw std::bad_alloc();
00727 #else
00728     return ptr;
00729 #endif
00730 }
00731 
00732 void* operator new[](size_t size, const char* file, int line)
00733 {
00734     void* ptr = alloc_mem(size, file, line, true);
00735 #if _DEBUG_NEW_STD_OPER_NEW
00736     if (ptr)
00737         return ptr;
00738     else
00739         throw std::bad_alloc();
00740 #else
00741     return ptr;
00742 #endif
00743 }
00744 
00745 void* operator new(size_t size) throw(std::bad_alloc)
00746 {
00747     return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00748 }
00749 
00750 void* operator new[](size_t size) throw(std::bad_alloc)
00751 {
00752     return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00753 }
00754 
00755 #if !defined(__BORLANDC__) || __BORLANDC__ > 0x551
00756 void* operator new(size_t size, const std::nothrow_t&) throw()
00757 {
00758     return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, false);
00759 }
00760 
00761 void* operator new[](size_t size, const std::nothrow_t&) throw()
00762 {
00763     return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, true);
00764 }
00765 #endif
00766 
00767 void operator delete(void* pointer) throw()
00768 {
00769     free_pointer(pointer, _DEBUG_NEW_CALLER_ADDRESS, false);
00770 }
00771 
00772 void operator delete[](void* pointer) throw()
00773 {
00774     free_pointer(pointer, _DEBUG_NEW_CALLER_ADDRESS, true);
00775 }
00776 
00777 #if HAVE_PLACEMENT_DELETE
00778 void operator delete(void* pointer, const char* file, int line) throw()
00779 {
00780     if (new_verbose_flag)
00781     {
00782         fast_mutex_autolock lock(new_output_lock);
00783         fprintf(new_output_fp,
00784                 "info: exception thrown on initializing object at %p (",
00785                 pointer);
00786         print_position(file, line);
00787         fprintf(new_output_fp, ")\n");
00788     }
00789     operator delete(pointer);
00790 }
00791 
00792 void operator delete[](void* pointer, const char* file, int line) throw()
00793 {
00794     if (new_verbose_flag)
00795     {
00796         fast_mutex_autolock lock(new_output_lock);
00797         fprintf(new_output_fp,
00798                 "info: exception thrown on initializing objects at %p (",
00799                 pointer);
00800         print_position(file, line);
00801         fprintf(new_output_fp, ")\n");
00802     }
00803     operator delete[](pointer);
00804 }
00805 
00806 void operator delete(void* pointer, const std::nothrow_t&) throw()
00807 {
00808     operator delete(pointer, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00809 }
00810 
00811 void operator delete[](void* pointer, const std::nothrow_t&) throw()
00812 {
00813     operator delete[](pointer, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00814 }
00815 #endif // HAVE_PLACEMENT_DELETE
00816 
00817 int __debug_new_counter::_S_count = 0;
00818 
00822 __debug_new_counter::__debug_new_counter()
00823 {
00824     ++_S_count;
00825 }
00826 
00831 __debug_new_counter::~__debug_new_counter()
00832 {
00833     if (--_S_count == 0 && new_autocheck_flag)
00834         if (check_leaks())
00835         {
00836             new_verbose_flag = true;
00837 #if defined(__GNUC__) && __GNUC__ >= 3
00838             if (!getenv("GLIBCPP_FORCE_NEW") && !getenv("GLIBCXX_FORCE_NEW"))
00839                 fprintf(new_output_fp,
00840 "*** WARNING:  GCC 3 or later is detected, please make sure the\n"
00841 "    environment variable GLIBCPP_FORCE_NEW (GCC 3.2 and 3.3) or\n"
00842 "    GLIBCXX_FORCE_NEW (GCC 3.4 and later) is defined.  Check the\n"
00843 "    README file for details.\n");
00844 #endif
00845         }
00846 }
00847 
00848 
00849 #endif
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Defines