home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / comp / lang / cplus / 16173 < prev    next >
Encoding:
Text File  |  1992-11-12  |  4.9 KB  |  160 lines

  1. Newsgroups: comp.lang.c++
  2. Path: sparky!uunet!charon.amdahl.com!pacbell.com!sgiblab!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!cbnewsk!pegasus!hansen
  3. From: hansen@pegasus.att.com (Tony L. Hansen)
  4. Subject: Re: Making qsort type-safe
  5. Organization: AT&T
  6. Date: Thu, 12 Nov 1992 19:42:07 GMT
  7. Message-ID: <1992Nov12.194207.19040@cbnewsk.cb.att.com>
  8. Summary: it can be done, almost
  9. Keywords: sorting, templates, qsort
  10. References: <1992Nov4.162131.10289@cs.brown.edu>
  11. Sender: hansen@cbnewsk.cb.att.com (tony.l.hansen)
  12. Lines: 146
  13.  
  14. < From: sdm@cs.brown.edu (Scott Meyers)
  15. < ...
  16. < So I thought of doing this:
  17. <
  18. <     template<class T>
  19. <     inline
  20. <     void safeQSort(T array[], int arraySize,
  21. <                    int (*cmp)(const T *val1, const T *val2)) 
  22. <     {
  23. <         qsort(array, arraySize, sizeof(T), cmp);
  24. <     }
  25. < However, this fails to compile, because the function pointer in safeQSort
  26. < isn't type-compatible with the one in qsort.  That is, there is no
  27. < automatic conversion from
  28. <     int (*)(const T*, const T*)
  29. < to
  30. <     int (*)(const void*, const void*)
  31. < I pacified the compiler with a cast:
  32. <     typedef int (*CMP)(const void*, const void*);
  33. <     template<class T>
  34. <     inline
  35. <     void safeQSort(T array[], int arraySize,
  36. <                    int (*cmp)(const T *val1, const T *val2)) 
  37. <     {
  38. <         qsort(array, arraySize, sizeof(T), (CMP) cmp);
  39. <     }
  40. < But casts make me queasy, especially so after the mail I got telling me
  41. < about bit pattern conversions when types change.  So -- is this a safe
  42. < thing to do, or does the devil lurk within?
  43.  
  44. Same trap. Consider a machine with the following bit pattern scenarios:
  45.  
  46.     A void* takes up 34 bits, encapsulated in a 64-bit quantity.
  47.  
  48.     However, a long* takes up 32 bits.
  49.  
  50.     When a void* gets converted to a long*, it's shifted right 2 bits
  51.     and the top 4 bytes lopped off. When a long* gets converted to a
  52.     void*, 4 bytes get added to the top and it's all shifted left 2
  53.     bits.
  54.  
  55. Now, consider what happens when sorting an array of longs, with the
  56. following comparison function:
  57.  
  58.     int lcmp(const long *a, const long *b)
  59.     { return *a - *b; }
  60.  
  61. When lcmp is invoked in your example above. The bit pattern passed to cmp
  62. on the stack will be
  63.  
  64.     +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
  65.     |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
  66.     |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
  67.     +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
  68.     
  69.     <---------------- a+0 -----------------> <---------------- a+1 ----------------->
  70.  
  71. But lcmp is only expecting 4 bytes per address, or
  72.  
  73.     +----+----+----+----+----+----+----+----+
  74.     |    |    |    |    |    |    |    |    |
  75.     |    |    |    |    |    |    |    |    |
  76.     +----+----+----+----+----+----+----+----+
  77.     
  78.     <------ a+0 -------> <------ a+1 ------->
  79.  
  80. In other words, the address associated with the first pointer will be
  81. interpretted as the two separate addresses.
  82.  
  83. The same trap as before is involved: an implicit type conversion has
  84. occurred via the stack without going through an explicit cast.
  85.  
  86. Sorry.
  87.  
  88. By the way, here's a completely typesafe safeQsort(). It uses a template
  89. helper class.
  90.  
  91. ----------------------------------------------------------------
  92. #include <stdlib.h>
  93. #include <iostream.h>
  94.  
  95. // template helper class
  96. template <class T>
  97. class safecmpclass
  98. {
  99. public:
  100.     static int cmp(const void*, const void*);
  101. };
  102.  
  103. // invoke the proper op< for the values
  104. template <class T>
  105. int safecmpclass<T>::cmp(const void *pa, const void *pb)
  106. {
  107.     const T *a = (const T*)pa;
  108.     const T *b = (const T*)pb;
  109.     return (*a < *b) ? -1 :
  110.        (*b < *a) ? 1 : 0;
  111. }
  112.  
  113. // the safe qsort() function
  114. template<class T>
  115. void safeQSort(T array[], int arraySize)
  116. {
  117.     qsort(array, arraySize, sizeof(T), safecmpclass<T>::cmp);
  118. }
  119.  
  120. // test it out
  121. main()
  122. {
  123.     // 30 random numbers
  124.     static long foo[30] = {
  125.     30639, 31938, 2675, 6031, 18500, 8736, 32276, 2499, 28250, 29533,
  126.     8213, 30663, 15787, 19914, 1511, 25501, 26627, 6526, 14472, 25778,
  127.     12134, 3604, 16527, 28926, 3841, 1833, 26361, 7136, 1745, 9364
  128.     };
  129.  
  130.     // sort it
  131.     safeQSort(foo, 30);
  132.  
  133.     // print out the sorted array
  134.     for (int i = 0; i < 30; i++)
  135.     cout << "foo[" << i << "] = " << foo[i] << "\n";
  136.     return 0;
  137. }
  138. ----------------------------------------------------------------
  139.  
  140. Is that the end of the story, though? No, of course not, because just
  141. swapping the bits around may not be sufficient to swap a[0] and a[1]. They
  142. may truly have to be swapped using assignments, and qsort provides no way to
  143. do that.
  144.  
  145. The bottom line is that trying to use the C function qsort() to implement a
  146. true qsort will always lead to a problem. However, if you limit yourself to
  147. using it only with simple structures, you can do it.
  148.  
  149.                     Tony Hansen
  150.                 hansen@pegasus.att.com, tony@attmail.com
  151.                 att!pegasus!hansen, attmail!tony
  152.