I'm writing a doubly linked list and a lot of the code ends up being the same with _next and _prev switched. What can I do to keep from writing some code with _next and then the same code again with _prev? Can I do like a template that flips the meaning of the pointers or something?

Recommended Answers

All 3 Replies

I can think of one way which would involve both _next and _prev being stored in an array, size 2.

The idea being that they're indexed by a boolean value, so that you can "flip" between the two. Your indexes might be node::pointer[true] for _next, and node::pointer[false] for _prev

You'd possibly need to do the same with your head and tail pointers in the main list class.

This, with a combination of templates would probably work - the templates merely to avoid the repeated code - then covered with some public method wrappers as a matter of hiding away this ugly little trick.
- whether or not it would be readable, is another matter entirely.

That worked, but I don't know if it's good or not. How can this be better?

#include <iostream>
#include <memory>

using namespace std;

namespace hamrick {
  namespace util {
    template <typename T>
    struct node {
      T _value;
      node *_pointer[2];

      node( T value, node *prev, node *next ): _value( value ) {
        _pointer[false] = prev;
        _pointer[true] = next;
      }
    };
  }

  template <typename T>
  class list {
  public:
    list(): _head( new util::node<T>( T(), 0, 0 ) ), _size( 0 ) {
      _head->_pointer[false] = _head.get();
      _head->_pointer[true] = _head.get();
    }

    T GetFront() { return _head->_pointer[true]->_value; }
    T GetBack() { return _head->_pointer[false]->_value; }
    void AddFront( T value ) { this->Add( value, _head.get(), _head->_pointer[true], true ); }
    void AddBack( T value ) { this->Add( value, _head->_pointer[false], _head.get(), false ); }
    void RemoveFront() { this->Remove( true ); }
    void RemoveBack() { this->Remove( false ); }
    bool IsEmpty() { return _head->_pointer[true] == _head.get(); }
    bool Verify();
  private:
    void Add( T value, util::node<T> *left, util::node<T> *right, bool next );
    void Remove( bool next );
    auto_ptr<util::node<T> > _head;
    int _size;
  };

  template <typename T>
  bool list<T>::Verify() {
    util::node<T> *rtemp = _head.get();
    util::node<T> *ltemp = _head.get();

    for ( int i = 0; i < _size; ++i ) {
      ltemp = ltemp->_pointer[false];
      rtemp = rtemp->_pointer[true];

      // shouldn't get to the head during this loop
      if ( ltemp == _head.get()
        || rtemp == _head.get() ) {

        return false;
      }
    }

    // the next pointer for both temps should be the head
    if ( ltemp->_pointer[false] != _head.get()
      || rtemp->_pointer[true] != _head.get() ) {

      return false;
    }

    return true;
  }

  template <typename T>
  void list<T>::Add( T value, util::node<T> *left, util::node<T> *right, bool next ) {
    util::node<T> *temp = new util::node<T>( value, left, right );
    _head->_pointer[next]->_pointer[!next] = temp;
    _head->_pointer[next] = temp;
    ++_size;
  }

  template <typename T>
  void list<T>::Remove( bool next ) {
    if ( _head->_pointer[next] != _head.get() ) {
      util::node<T> *temp = _head->_pointer[next];
      _head->_pointer[next] = temp->_pointer[next];
      temp->_pointer[next]->_pointer[!next] = _head.get();
      delete temp;
      --_size;
    }
  }
}

int main() {
  hamrick::list<int> l;

  if ( !l.Verify() ) {
    cout<<"Invalid list after construction"<<endl;
  }

  for ( int i = 0; i < 10; ++i ) {
    l.AddFront( i );
    if ( !l.Verify() ) {
      cout<<"Invalid list after inserting "<< i <<endl;
    }
  }

  while ( !l.IsEmpty() ) {
    int front = l.GetFront();
    cout<< front <<" ";
    l.RemoveFront();
    if ( !l.Verify() ) {
      cout<<"Invalid list after removing "<< front <<endl;
    }
  }

  cout<<endl;
}

I don't really know how I should add comments either so there aren't many.

I haven't tested it, but it looks good to me.

As far as commenting is concerned, the minimalist approach is better IMHO. They're best reserved for nontrivial bits of code where you're likely to come back in 6 months time and wonder what it does.
In places where code speaks for itself, comments are fairly superfluous.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.