next up previous contents
Next: A Second Run-Time Example: Up: Arbitrary Graph Objects Via Previous: Altering the Graph Class

A First Run-Time Example:

To use this kind of code, look at the new run method in our simulation. For convenience, we define the needed access functions as global to the run.c file by placing them before the defintion of the callback function run(). We begin with a simple application that adds float * data to the edges and double * data to the nodes. This requires that we write float * and double * access functions as listed below:

#include "simulation.h"

//set up Arbent handling methods
int float_equal(float *a,float *b)
{ return(*a == *b);}
int double_equal(double *a,double *b)
{ return(*a == *b); }

void float_entry(float **a,float *b)
{ (float *)*a = new float; *a = b;}

void double_entry(double **a,double *b)
{ (double *)*a = new double; *a = b;}

ostream& float_show(ostream& out, float *a)
{ out << *a << endl; return out; }

ostream& double_show(ostream& out, double *a)
{ out << *a << endl; return out; }

The application code is very similar:

void run(Widget w,XtPointer client_data,XtPointer call_data)
{
  XmPushButtonCallbackStruct *P = (XmPushButtonCallbackStruct *)call_data;
  application_data *T = (application_data *)client_data;
  Vertex center_in,center;
  Display *display = T->display;
  Drawable drawable = T->drawable;
  Pixmap pixmap1 = T->pixmap;
  Pixmap pixmap2 = T->pixmap2;
  GC gc = T->gc;
  
  center.x = 25.0;
  center.y = 25.0;
  Pixel graph_colors[4];
  for(int i=0;i<4;++i)
    graph_colors[i] = T->pParts[i]->pixel;
  if(T->T==NULL){
    printf("Building the XGraph object.\n");
    T->T = new Graph(EQ_FN(float_equal),DISP_FN(float_show),
                     ENTRY_FN(float_entry),
                     EQ_FN(double_equal),DISP_FN(double_show),
		     ENTRY_FN(double_entry),
                     T->radius,T->center,        //node circle
                     T->foreground,              //fg
                     T->background,              //bg
                     graph_colors,               //graph colors
                     T->display,                 //graphics
                     T->drawable,
                     T->pixmap,
                     T->pixmap2,
                     T->gc);
    }

Note the 6 arguments in the Graph constructor which are used to pass in the appropraite edge and node access functions. Next, we initialize the edge data with float and the node data with double values as follows:

    
  int NumberNodes = T->T->NumberNodes();
  int NumberEdges = T->T->NumberEdges();
  float *edge_data = new float[NumberEdges];
  double *node_data = new double[NumberNodes];

  for(int i = 0;i< NumberNodes; ++i)
    node_data[i] = 50.0;

  for(int i = 0;i< NumberEdges; ++i)
    edge_data[i] = 25.0;
    
  T->T->FillData(node_data,sizeof(double),edge_data,sizeof(float)); 
     
  ...code unchanged..
     
}

Note that once the edge_data and node_data were constructed and initialized, we call the Graph method FillData to initialize the edge and node void * data fields in each node and defined edge for the graph. Let's look at the code that implements the method FillData.

Graph& Graph::FillData(Arbent node_data,int size_node,
                       Arbent edge_data,int size_edge)
{
  int i = 0;
  GnodePtrBagIter nit(nodes());
  for( ; nit; ++nit){
    nit()->entry(&(nit()->data),node_data + i*size_node);
    nit()->show(cout,nit()->data);
    ++i;
    }
  i = 0;
  for(GedgePtrBagIter eit(edges());eit;++eit){
    eit()->entry(&(eit()->data), edge_data + i*size_edge);
    eit()->show(cout,eit()->data);
    ++i;
    }
  return *this;
}

Note that the node and edge data is sent in as void * so its sizeof information is lost. Hence, we send in the appropriate size information as part of the prototype. Also, note that it is very important for each node and edge to fully understand the type of data that is being used. Look at the lines:

    nit()->entry(&(nit()->data),node_data + i*size_node);
    eit()->entry(&(eit()->data), edge_data + i*size_edge);

This is where the void * data types are interpreted correctly: The node data is to be type double *. Hence the access function nit()->entry() is used to grab the correct access function for this data. Look at the entry code:

void float_entry(float **a,float *b)
{ (float *)*a = new float; *a = b;}

Note we send in the address of the void * data and the actual double * information we wish to use into the entry method. Then we cast the void * data to the correct double * type and set it equal to an appropriate constructor. Note that all of this detail in hidden in the access functions and is not present in the graph class itself. That is the important point.

This code generates the following results for a simple graph of 8 nodes and 7 edges:

Number of Nodes in the Graph is 8
Number of Edges in the Graph is 7

Graph: 
Nodes: 
  n8 0   
  n7 1
  n6 4
  n5 9  <=== node value as a double via entry()
  n4 16
  n3 25
  n2 36
  n1 49

  Edges:  n2-----(1.33313)---> n1 0

          n3-----(0.569474)---> n4 1

          n4-----(2.54805)---> n6 2

          n5-----(1.50698)---> n3 3  <=== edge value as float via entry()

          n6-----(0.782686)---> n7 4

          n7-----(2.17525)---> n2 5

          n8-----(3.43615)---> n2 6

End Graph


next up previous contents
Next: A Second Run-Time Example: Up: Arbitrary Graph Objects Via Previous: Altering the Graph Class
Jim Peterson
1999-05-17