| 
 
| 
|  | A Simple Association Template for C++ Submitted by
 |  
  
 At my previous employer, we used visual development tools to take UML and
generate source code to implement some of the more complex relationships
that UML can define but C++ doesn't have built in to the language.  When
I moved to my new employer, I found myself particularly missing simple 
associations, which are, essentially, a two-way pointer.  So I set out 
to write up code to model them in C++ using templates.
 
 A simple association is a simple, but powerful design tool and having an
efficient implementation can be quite helpful.  This template implements
a simple (one to one) association.  It can be used with other container
classes to implement multiple or complex associations.  I have included
a sample main program which creates some dummy classes and tests things out.
I figured that this code is something that not too many C++ developers have
ever considered doing, but might find very useful.  Enjoy.
 
 Greg Taylor
 gtaylor@falcon.csc.calpoly.edu
 |  
 
| Currently browsing [assoc.zip] (2,870 bytes) - [assoc.h] - (3,586 bytes) 
 
 | // assoc.h
//
// A simple association template
//
// Provides a simple 1 to 1 association between two container classes.  If
// one end of the association is set or broken, the other end is updated to
// match.  Assertions check that the application programmer doesn't attempt
// to double-set an association and other failure states.
//
// Instanciation:
// Provide a member in each container class of type 
//   assoc<myContainer type, associatedContainer type>
//
// This member must be constructed with its parameterized constructor,
// which takes a single parameter, a pointer to the container class (this).
//
// Public methods:
//
// link(assoc<other,my> *otherEnd) - This method sets up the linkage between
//   the two container classes.  Its argument is a pointer to the association
//   member in the other class.  Calling this method on one end of the 
//   association will set up both ends, so it only need be called once per
//   association.
//
// unlink(assoc<other,my> *otherEnd) - De-associates the two container classes.
//   There are two versions of this method, one with a parameter, which is 
//   simply the same pointer that was used to set up the association (a pointer
//   to the other end) and one without.  The parameterized version is safer to
//   use simply because it hs more assertion error checks, which may help to 
//   catch bugs earlier.
//
// Lastly, there is an overloaded type-cast for the association class to the
// type of the associated container.  This is useful for preserving the abstracted
// concept of the association.  It will return NULL if no association is currently
// set. The 'get' method returns the same thing.
#ifndef SIMPLE_ASSOCIATIONTEMPLATE
#define SIMPLE_ASSOCIATIONTEMPLATE
 #include <assert.h>
 
 #ifndef NULL
#define NULL 0
#endif
 
 template <class myContainer, class otherContainer>
class assoc
{
  private:
    myContainer *containedIn;
    assoc<otherContainer, myContainer> *linkedTo;
 
 void linkToMe(assoc<otherContainer, myContainer> *otherEnd) {
      assert(otherEnd != NULL);
      assert(linkedTo == NULL);
     
      linkedTo = otherEnd;
    }
 
 void unlinkFromMe(assoc<otherContainer, myContainer> *otherEnd) {
      assert(otherEnd != NULL);
      assert(linkedTo == otherEnd);
 
 linkedTo = NULL;
    }
 
 myContainer *getContainer() {
      return containedIn;
    }
 
 public:
    assoc(myContainer *myOwner) {
      assert(myOwner != NULL);
      containedIn = myOwner;
      linkedTo = NULL;
    }
 
 ~assoc() {
      if (linkedTo != NULL) {
        linkedTo->unlinkFromMe(this);
      }
    }
    
    void link(assoc<otherContainer, myContainer> *target) {
      assert(target != NULL);
      assert(linkedTo == NULL);
 
 linkedTo = target;
      target->linkToMe(this);
    }
 
 void unlink(assoc<otherContainer, myContainer> *target) {
      assert(target == linkedTo);
      assert(linkedTo != NULL);
 
 target->unlinkFromMe(this);
      linkedTo = NULL;
    }
 
 void unlink() {
      // The unlink method with a parameter is prefered because it does additional
      // error checking.  Either method will accomplish the same task, however.
      assert(linkedTo != NULL);
 
 linkedTo->unlinkFromMe(this);
      linkedTo = NULL;
    }
 
 operator otherContainer *() {
      if (linkedTo == NULL)
        return NULL;
      return linkedTo->getContainer();
    }
 
 otherContainer *get() {
      if (linkedTo == NULL)
        return NULL;
      return linkedTo->getContainer();
    }
 
 friend class assoc<otherContainer, myContainer>;
};
 
 #endif
 | 
 |  
 
| Currently browsing [assoc.zip] (2,870 bytes) - [AssocTestMain.cpp] - (2,293 bytes) 
 
 | // Simple association tester and example usage code
#include "assoc.h"
#include <stdio.h>
 // two dummy classes that might have an association to each other.
class Edge;
 
 class Vertex
{
public:
  float x, y, z ;
  assoc<Vertex,Edge> edge;
 
 Vertex() : edge(this) {
    x = 0.0f;
	y = 0.0f;
	z = 0.0f;
  }
};
 
 class Edge
{
public:
  float angle;
 
 assoc<Edge,Vertex> vertex;
 
 Edge() : vertex(this) {
    angle = 0;
  }
};
 
 // Test/example usage 'main' for simple associations.
int main () 
{
  Vertex v1;
  Edge e1;
  Vertex *pv;
  Edge *pe;
 
 // Set up an edge and vertices 
  v1.x = 24.0f;
  v1.y = 14.0f;
  v1.z =  4.0f;
  e1.angle = 0.5;
 
 // Associate v1 and e1
  v1.edge.link(&e1.vertex);
 
 // Test association v1/e1 directly
  printf("This should be %g => %g\n", v1.x, e1.vertex.get()->x);
  printf("and this should be %g => %g\n", e1.angle, v1.edge.get()->angle);
 
 // That interface can be clumbsy so it can be cleaner to use the
  // overloaded cast operation.
  float i = ((Edge *)v1.edge)->angle;
 
 // Or for the cleanest referencing, use pointers
  pv = e1.vertex;
  pe = v1.edge;
 
 // Print out verification of expected results
  printf("This should be %g => %g\n", v1.y, pv->y);
  printf("and this should be %g => %g\n", e1.angle, pe->angle);
 
 // Now test clearing the association
  pe->vertex.unlink(&pv->edge);
 
 if (e1.vertex == NULL && v1.edge == NULL)
    printf("We successfully cleared the association\n");
  else
    printf("Test FAILED.\n");
 
 // Re-create the association.
  v1.edge.link(&e1.vertex);
 
 // Now break it another way
  v1.edge.unlink();
 
 // Test again
  if (e1.vertex == NULL && v1.edge == NULL)
    printf("We successfully cleared the association\n");
  else
    printf("Test FAILED.\n");
 
 // Now test the destructor
  Vertex *v2 = new Vertex;
 
 v2->z = 3.1415f;
  v2->edge.link(&e1.vertex);
  pv = e1.vertex;
 
 // Print out verification of expected results
  printf("This should be %g => %g\n", v2->z, pv->z);
  printf("and this should be %g => %g\n", e1.angle, pe->angle);
 
 delete v2;
 
 if (e1.vertex == NULL)
    printf("We successfully cleared the association\n");
  else
    printf("Test FAILED.\n");
 
 return 0;
}
 | 
 |  The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
 
 |