|
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.
|