home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / lang / cplus / 12547 < prev    next >
Encoding:
Text File  |  1992-08-19  |  6.7 KB  |  249 lines

  1. Path: sparky!uunet!zephyr.ens.tek.com!uw-beaver!cornell!batcomputer!caen!sdd.hp.com!swrinde!elroy.jpl.nasa.gov!decwrl!deccrl!bloom-beacon!eru.mt.luth.se!lunic!sunic!seunet!appli!niklas
  2. From: niklas@appli.se (Niklas Hallqvist)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: Doubly-linked list trick (was Re: Garbage Collection for C++)
  5. Message-ID: <2134@appli.se>
  6. Date: 19 Aug 92 08:33:14 GMT
  7. References: <1992Aug14.021547.15215@news.mentorg.com>> <DAVEG.92Aug14194411@synaptx.synaptics.com> <2009@appli.se> <16ovt2INN3nq@early-bird.think.com>
  8. Organization: Applitron Datasystem AB, GOTHENBURG, SWEDEN
  9. Lines: 238
  10.  
  11. barmar@think.com (Barry Margolin) writes:
  12.  
  13. >In article <2009@appli.se> niklas@appli.se (Niklas Hallqvist) writes:
  14. >>When you want to do a doubly linked list to the cost of a singly
  15. >>linked one (i.e. in mem. space).  Instead of having two pointers
  16. >>for the link in each node, take the "previous" and "next" pointers
  17. >>and XOR them, and store the result!
  18.  
  19. >Problems:
  20.  
  21. >1) XOR isn't defined for pointers in C/C++.
  22.  
  23. True!
  24.  
  25. >2) You could cast the pointers to suitably large integers and XOR those,
  26. >but the result of casting the integers back into pointers is
  27. >implementation-dependent or undefined (I don't remember which, but either
  28. >way it's not portable).
  29.  
  30. Well, read the ARM 5.4:
  31.  
  32.     A pointer converted to an integer of sufficient size (if any such
  33.     exists on the implementation) and back to the same pointer type
  34.     WILL have its original value; mappings between pointers and integers
  35.     are OTHERWISE implementation dependent.
  36.  
  37. Emphasises mine!  This means the trick WILL be portable to all platforms
  38. where there exists an integral type large enough (ie. almost all platforms).
  39. I agree that this may be a bad habit, BUT... what I wanted to tell was:
  40. It's REAL HARD (read: impossible) to implement GC on most architectures if
  41. this rule is left in.
  42.  
  43. >3) It's only useful if you always traverse the list from one end or the
  44. >other, since you need to know what to XOR each combined pointer with in
  45. >order to get one of the original pointers.  But one of the main reasons for
  46. >using a doubly-linked list is so that you can start with any entry in the
  47. >list and get to the rest.
  48.  
  49. Often you don't start with the entry, but rather a pointer to the entry.
  50. In C++ you can make smart-pointers, no big deal...
  51.  
  52. >3a) In particular, how would you do a circular doubly-linked list with this
  53. >scheme?  When it starts out, the node's previous and next pointers would
  54. >both be the same (they point to itself) and the result of the XOR would be
  55. >0.  But that's also the result when the circular list has two elements,
  56. >since the previous and next nodes would always be the "other" node.
  57.  
  58. You confused me here... I just had to go home to my Amiga and implement the
  59. thing (quick and dirty, I admit) to see why it works.  Of course, it's a
  60. smart-pointer keeping the contextual information needed.  Look for yourself,
  61. the code has been compiled with GCC (version >= 2.1):
  62.  
  63. Niklas
  64.  
  65. #include <iostream.h>
  66. #include <stdlib.h>
  67. #include <assert.h>
  68.  
  69. template <class Int, class Elem>
  70. class Node {
  71.   Int x;
  72.   Elem e;
  73. public:
  74.   Node(Elem el) : e(el), x(0) { assert(sizeof(Int) >= sizeof(Node*)); }
  75.   Node* link(Node* n) const { return (Node*)(x ^ (Int)n); }
  76.   void link(Node* l, Node* r) { x = (Int)l ^ (Int)r; }
  77.   void relink(Node* o, Node* n) { x ^= (Int)o ^ (Int)n; }
  78.   Elem elem() const { return e; }
  79. };
  80.  
  81. template <class Int, class Elem>
  82. class Ptr {
  83.   Node<Int, Elem>* p;
  84.   Node<Int, Elem>* r;
  85. public:
  86.   Ptr() : p(0), r(0) {}
  87.   Ptr(const Ptr& ptr) : p(ptr.p), r(ptr.r) {}
  88.   Ptr(Node<Int, Elem>* n) : p(n), r(0) {}
  89.   Ptr operator=(const Ptr& ptr) { p = ptr.p; r = ptr.r; return *this; }
  90.   Ptr operator=(Node<Int, Elem>* n) { p = n; r = 0; return *this; }
  91.   int operator==(const Ptr& ptr) { return p == ptr.p; }
  92.   int operator!=(const Ptr& ptr) { return p != ptr.p; }
  93.   Node<Int, Elem>* operator->() const { return p; }
  94.   Node<Int, Elem>& operator*() const { return *p; }
  95.   Ptr operator++()
  96.     { Node<Int, Elem>* n = p; p = r; r = p->link(n); return *this; }
  97.   Ptr operator--()
  98.     { Node<Int, Elem>* n = r; r = p; p = r->link(n); return *this; }
  99.   int operator!() const { return !p; }
  100.   operator Node<Int, Elem>*() const { return p; }
  101.   void next(Node<Int, Elem>* n) { r = n; }
  102. };
  103.  
  104. template <class Int, class Elem>
  105. class List {
  106.   Ptr<Int, Elem> p;
  107. public:
  108.   void prepend(Elem);
  109.   void append(Elem);
  110.   Ptr<Int, Elem> head() const { return p; }
  111.   Ptr<Int, Elem> tail() const
  112.     { if (p) { Ptr<Int, Elem> ptr(p); return --ptr; } else return 0; }
  113. };
  114.  
  115. template <class Int, class Elem>
  116. void List<Int, Elem>::prepend(Elem e)
  117. {
  118.   Node<Int, Elem>* n = new Node<Int, Elem>(e);
  119.   Ptr<Int, Elem> old = p;
  120.   p = n;
  121.   if (old)
  122.     {
  123.       Ptr<Int, Elem> prev = old;
  124.       --prev;
  125.       p->link(prev, old);
  126.       p.next(old);
  127.       prev.next(p);
  128.       prev->relink(old, p);
  129.       old->relink(prev, p);
  130.     }
  131.   else
  132.     p.next(p);
  133. }
  134.  
  135. template <class Int, class Elem>
  136. void List<Int, Elem>::append(Elem e)
  137. {
  138.   Ptr<Int, Elem> n = new Node<Int, Elem>(e);
  139.   if (p)
  140.     {
  141.       Ptr<Int, Elem> prev = p;
  142.       --prev;
  143.       n->link(prev, p);
  144.       n.next(p);
  145.       prev.next(n);
  146.       prev->relink(p, n);
  147.       p->relink(prev, n);
  148.     }
  149.   else
  150.     {
  151.       p = n;
  152.       p.next(p);
  153.     }
  154. }
  155.  
  156. template <class Int, class Elem>
  157. void print_forward(ostream& os, const List<Int, Elem>& l)
  158. {
  159.   os << "[ ";
  160.   Ptr<Int, Elem> p = l.head();
  161.   if (p)
  162.     while (1)
  163.       {
  164.     os << p->elem();
  165.     if (++p == l.head())
  166.       break;
  167.     os << ", ";
  168.       }
  169.   os << " ]" << endl;
  170. }
  171.  
  172. template <class Int, class Elem>
  173. void print_backward(ostream& os, const List<Int, Elem>& l)
  174. {
  175.   os << "[ ";
  176.   Ptr<Int, Elem> p = l.tail();
  177.   if (p)
  178.     while (1)
  179.       {
  180.     os << p->elem();
  181.     if (--p == l.tail())
  182.       break;
  183.     os << ", ";
  184.       }
  185.   os << " ]" << endl;
  186. }
  187.  
  188. template <class Int, class Elem>
  189. void print_funny(ostream& os, const List<Int, Elem>& l)
  190. {
  191.   int forward = 1;
  192.   os << "[ ";
  193.   Ptr<Int, Elem> p = l.tail();
  194.   if (p)
  195.     while (1)
  196.       {
  197.     os << p->elem();
  198.     if (forward)
  199.       ++p;
  200.     else
  201.       {
  202.         --p;
  203.         --p;
  204.         --p;
  205.       }
  206.     forward = !forward;
  207.     if (p == l.tail())
  208.       break;
  209.     os << ", ";
  210.       }
  211.   os << " ]" << endl;
  212. }
  213.  
  214. template<class Int, class Elem>
  215. inline void print(ostream& os, const List<Int, Elem>& l)
  216. {
  217.   print_forward(os, l);
  218.   print_backward(os, l);
  219.   print_funny(os, l);
  220. }
  221.  
  222. int main()
  223. {
  224.   List<int, int> l;
  225.   print(cout, l);
  226.   l.prepend(4);
  227.   print(cout, l);
  228.   l.prepend(3);
  229.   print(cout, l);
  230.   l.prepend(2);
  231.   print(cout, l);
  232.   l.prepend(1);
  233.   print(cout, l);
  234.   l.append(5);
  235.   print(cout, l);
  236.   l.append(6);
  237.   print(cout, l);
  238.   l.append(7);
  239.   print(cout, l);
  240.   l.append(8);
  241.   print(cout, l);
  242.   return 0;
  243. }
  244. -- 
  245. Niklas Hallqvist    Phone: +46-(0)31-40 75 00
  246. Applitron Datasystem    Fax:   +46-(0)31-83 39 50
  247. Molndalsvagen 95    Email: niklas@appli.se
  248. S-412 63  GOTEBORG, Sweden     mcsun!seunet!appli!niklas
  249.