\documentclass{article}
\usepackage{epsfig}
\usepackage{latexsym}
%\textheight 4.8in
%\topmargin -0.25in

\topmargin=-.6in
\textheight=9.0in
\textwidth=5.5in
\oddsidemargin=.5in

\title{Matrix-Vector Product: A Small Example\\
 of Parallel Processing Using CORBA}

\author{David R. Musser}

\newcommand{\code}[1]{\texttt{#1}}

\begin{document} \maketitle

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Introduction}

In this note I describe a distributed version of a matrix-vector
product computation, devised as simple demonstration of a CORBA
client-server system in which the client performs an algorithm that
distributes parts of a large computation to two or more servers.  The
servers are functionally identical, but they can be compiled with
CORBA implementations on different operating systems and run on
different machines from the client and from each other.  With a bit
more work it should be possible also to make them work with different
CORBA implementations and/or with different parts implemented in
different languages.  In this note the omniORB CORBA implementation is
used; the example has also been compiled and tested with the MICO ORB,
but not with both ORBs involved in the same computations.  The
implementation language in all cases is C++, but in some experiments
two different C++ compilers have been used; e.g., Microsoft Visual C++
6.0 on Windows 95 and GNU C++ 2.8.1 on SunOS.

These programs are only intended as simple exercises aimed at
exploring ideas about combining generic programming techniques with
distributed computing.  For a serious effort at providing distributed
components for linear algebra, one should start with close examination
of efforts such as the PETSc
Library,\footnote{http://www-unix.mcs.anl.gov/petsc} which does
distributed computation but doesn't employ generic programming, or the
Matrix Template Library
(MTL),\footnote{http://lsc.nd.edu/research/mtl} which makes extensive
use of generic programming but doesn't (yet) do distributed
computation.

Besides having the flexibility to have servers and clients
inter-operating on different machines or operating systems, this
example provides further opportunities for generality.  Varying the
type of data elements is one possibility.  Another, with which the
term ``generic programming,'' is perhaps most often identified, is
varying the type of sequence data structure used, without having to
change the algorithm(s) involved; that is, the algorithms are generic
with respect to different data structures.  As currently written, the
program uses the vector representation from the C++ standard library,
but the product algorithm would work equally well with a standard
library list representation. (Actually this is not quite true with the
algorithm is described here, but with small modifications we could
obtain an algorithm that would work with both a list and a vector
representation.)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Client-server structure}

The distributed matrix-product system consists of:
\begin{itemize}

 \item A server that provides a matrix object equipped with a
\verb|block_product| function, which is a function that is able to use
a designated range of rows of a matrix \verb|m| and a column vector
\verb|c| and produce the corresponding range of elements of the column
vector \verb|r| that is the matrix-vector product of \verb|m| and
\verb|c|.  This server registers the object reference of the
matrix with the CORBA Naming Service, using a name specified by
the user on the command line.  One
should start two copies of the server, each with a different name;
e.g.,
\begin{verbatim}
  server matrix1
  server matrix2
\end{verbatim}
  
 \item A client that connects to two matrix servers holding the same
matrix and uses their matrix \verb|block_product| functions.  The call
of the first server's \verb|block_product| function computes the top
half of a matrix-vector product and the call on the second server does
the second half.  These calls are done in parallel, by making the
first call via the Dynamic Invocation Interface, as described later.
One tells the clients the names of the servers with two parameters
on the command line, and the client uses the Naming Service to
look them up and obtain the corresponding object references.

\end{itemize}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{IDL definitions}

For this simple example, the entire IDL specification consists of just
these two parts:\footnote{Throughout we present code in a variant of
Knuth's literate programming style \cite{Knuth:literate}, in which
code is presented in ``parts'' numbered according to the page number
on which they appear (with parts on the same page distinguished by
appending a letter to the number).  This form of presentation is
supported by Briggs' Nuweb tool \cite{Briggs} (slightly modified) with
which we also generate all code files directly from the document's
source file.  Parts may refer to other parts that have either already
been defined or are defined later in the document; one can choose
whatever order of presentation seems best for expository purposes,
while the order in which code appears in the compilation files is a
matter of how the parts are ordered when included in file parts such
as \code{matrix.idl}.  See also Appendix~\ref{nuweb:hints}.  (Even
experienced Nuweb users should see the note there about problems with
including line directives in IDL files.)}

@o matrix.idl 
@{@<Row and Column types@>
@<Matrix interface@>@}

The interfaces of the CORBA objects used in this example are expressed
in CORBA's Interface Definition Language (IDL).  First we define
\code{Row} and \code{Column} objects in terms of IDL's built-in
\code{Sequence} type:

@d Row and Column types
@{typedef sequence<double> Row;
typedef sequence<double> Column;@}

The IDL \code{sequence} type is similar in its functionality to the
C++ standard library \code{vector} type but is more limited.  In a
later section we consider the possibility of defining an IDL interface
for (special cases of) the \code{vector} type and redoing this matrix
example in terms of it.

We use \code{double} is used as the type of row, column, and matrix
elements.  CORBA has no mechanism for compile-time type
parameterization analogous to C++ templates or Ada generics, but we
could make this example more generic by using its dynamic type system,
using type \code{Any} in place of \code{double}.  This is left for a
future extension.

We next define the \code{Matrix} type, essentially as a
\code{sequence} of \code{Row}'s.  Actually, we define \code{Matrix}
not just with a \code{typedef} but with an \code{interface}
definition, which allows us to provide its objects with operations we
intend to provide:

@d Matrix interface
@{interface Matrix {
   typedef sequence<Row> rep;
   Column block_product(in unsigned long first, 
                        in unsigned long last,
                        in Column c);
};@}

In this case we specify only one operation, \code{block\_product},
which takes two parameters of type \code{unsigned long}
and one of type \code{Column}; it returns an object of type
\code{Column}.

The \code{rep} type is for use in the implementation, but in a larger
example there might be operations that have this as a parameter
type or return type.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Matrix server implementation} 

In the server program we provide an implementation of the
\code{block\_product} function, set up a matrix, and initialize to
perform the \code{block\_product} operation on the matrix at the
request of clients.  The overall structure of the server is as
follows.

@o server.cpp -d
@{@<Include standard and CORBA library headers@>
@<Include matrix object interface generated by IDL compiler@>
@<Include auxiliary file to help with using the Naming Service@>
@<Define matrix object implementation@>

int main(int argc, char *argv[])
{
  assert(argc == 2);
  const int N = 20;
  @<Set up matrix m0@>
  @<Initialize ORB connection@>
  @<Use m0 to initialize a matrix object, matrix1, and serve it up@>
  @<Register matrix1 with the Naming Service@>
  boa->impl_is_ready();
  return 0;
}@}

For this simple example only a few header files are needed.

@d Include standard and CORBA library headers
@{#include <vector>
#include <iostream.h>
using namespace std;
#pragma warning (disable : 4786)
#include <omniORB2/CORBA.h>
#include "omnithread.h"@}

Note that \code{<iostream.h>} is included, not the now-standard
\code{<iostream>}, because the latter leads to compilation errors
under VC++.  This might need to be changed when using another
compiler.

@d Include matrix object interface generated by IDL compiler
@{#include "matrix.h"@}

To keep the example simple, we construct the matrix that
\code{block\_product} operates on in the server.

@d Set up matrix m0
@{vector<double> r;
vecrep m0;

for (int i = 0; i < N; ++i) {
  r.erase(r.begin(), r.end());
  for (int j = 0; j < N; ++j)
    r.push_back(i*N + j);
  m0.push_back(r);
}@}

The server initialization is standard.

@d Initialize ORB connection
@{cout << "Initializing..." << endl;
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB2");
CORBA::BOA_var boa = orb->BOA_init(argc, argv, "omniORB2_BOA");@}

The type used in the following initialization is
\code{Matrix\_impl}, which is implemented by a class derived,
as required by the omniORB object interface
conventions, from the class \code{sk\_Matrix} generated
by the omniORB IDL compiler.

@d Use m0 to initialize a matrix object, matrix1, and serve it up
@{Matrix_impl matrix1(m0);
matrix1._obj_is_ready(boa);@}

@d Register matrix1 with the Naming Service
@{Matrix_var myobjRef = matrix1._this();
if (!bindObjectToName(orb, myobjRef, "test", argv[1])) {
  return 1;
}@}

@D Define matrix object implementation
@{typedef vector<vector<double> > vecrep;

@<Define inner product function@>

class Matrix_impl : virtual public _sk_Matrix {
    rep mr;
    int _calls;
public:
    @<Constructor, for conversion from vector representation@>
    @<Block product member function@>
};@}

The constructor copies from a matrix given in vector form to
a representation in terms of the CORBA sequence type.

@d Constructor, for conversion from vector representation
@{Matrix_impl (const vecrep& m0)
{
  _calls = 0;
  mr.length(m0.size());
  for (int i = 0; i < m0.size(); ++i) {
    mr[i].length(m0[i].size());
    for (int j = 0; j < m0[i].size(); ++j)
      mr[i][j] = m0[i][j];
  }
}@}

@d Block product member function
@{Column* block_product(CORBA::ULong first, 
                      CORBA::ULong last, const Column& c )
{
  cout << "block_product(" << first << ", " << last << ", c) start" 
       << endl;
  Column* z = new Column;
  z->length(last - first);
  int k = 0;
  for (int i = first; i != last; ++i, ++k) 
    (*z)[k] = inner_product(mr[i], c);
  cout << "block_product(" << first << ", " << last << ", c) end" 
       << endl;
  return z;
}@}

Finally, the inner product function used in computing the block product
is written in terms of the Row and Column types.

@d Define inner product function
@{double inner_product(const Row& r, const Column& c) {
  double result = 0;
  for (int i = 0; i < r.length(); ++i)
    result += r[i] * c[i];
  return result;
}@}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Matrix client}

This is a client that connects to two matrix servers
holding the same matrix and uses their matrix \code{block\_product}
functions.  The call of the first server's \code{block\_product} 
function computes the top half of a matrix-vector product
and the call on the second server does the second
half.  These calls are done in parallel, making use of a 
feature of the Dynamic Invocation Interface.

@O client.cpp -d
@{@<Include standard and CORBA library headers@>
@<Include matrix object interface generated by IDL compiler@>
@<Include auxiliary file to help with using the Naming Service@>

int main( int argc, char *argv[] )
{
  @<Initialize ORB connection@>
  vector<Matrix_var> client;
  @<Initialize client[0] and client[1] using Naming Service@>
  const int N = 20;
  @<Create a column of all 3's@>
  cout << "Computing the matrix-vector product..." << endl;
  vector<Column*> result(2);
  @<Compute top and bottom halves of matrix product in parallel@>
  @<Output first half of result from result[0], second half from result[1]@>
  cout << endl;
  return 0;
}@}

@D Initialize client[0] and client[1] using Naming Service
@{int j;
for (j = 1; j <= 2; ++j) {
  cout << "j = " << j << endl;
  try {
    CORBA::Object_var obj = getObjectReference(orb, "test", argv[j]);
    client.push_back(Matrix::_narrow(obj));
    if (CORBA::is_nil(client[j-1])) {
      cerr << "Cannot invoke on a nil object reference.\n" << endl;
      return 1;
    }
    assert(!CORBA::is_nil(client[j - 1]));
  }
  catch(CORBA::COMM_FAILURE& ex) {
    cerr << "Caught system exception COMM_FAILURE, unable to contact the "
         << "object." << endl;
    return 1;
  }
  catch(omniORB::fatalException& ex) {
  cerr << "Caught omniORB2 fatalException. This indicates a bug is caught "
       << "within omniORB2.\nPlease send a bug report.\n"
       << "The exception was thrown in file: " << ex.file() << "\n"
       << "                            line: " << ex.line() << "\n"
       << "The error message is: " << ex.errmsg() << endl;
    return 1;
  }
  catch(...) {
    cerr << "Caught a system exception." << endl;
  }
}@}

We create (arbitrarily) a \code{Column} to send to the servers
in the \code{block\_product} calls.

@d Create a column of all 3's
@{Column c;
c.length(N);
for (int i = 0; i < N; ++i) 
  c[i] = 3;@}

For the \code{block\_product} calls, we want to do the equivalent of
\begin{verbatim}
  result[0] = client[0]->block_product(0, N/2, c);
  result[1] = client[1]->block_product(N/2, N, c);
\end{verbatim}
except have them execute in parallel.  To achieve this we do the first
\code{block\_product} using the DII and send the request with
\code{send\_deferred} instead of (the normal) invoke call.  Thus, the
first call does not block and we can proceed with the second
\code{block\_product} (calling it in the usual way).

@d Compute top and bottom halves of matrix product in parallel
@{@<Set up first half with DII request@>
@<Asynchronously do block\_product on second server@>
@<Wait for the first block\_product call to finish@>
@<Check for exceptions@>
@<Get the return value from the first half@>@}

@d Set up first half with DII request
@{CORBA::Request_var _req = client[0]->_request( "block_product" );
_req->add_in_arg( "first" ) <<= (CORBA::ULong)0;
_req->add_in_arg( "last" ) <<= (CORBA::ULong)(N/2);
_req->add_in_arg( "c" ) <<= c;
_req->set_return_type(_tc_Column);
_req->send_deferred();@}

@d Asynchronously do block\_product on second server
@{result[1] = client[1]->block_product(N/2, N, c);@}

@d Wait for the first block\_product call to finish
@{_req->get_response();@}

@D Check for exceptions
@{if(_req->env()->exception()) {
  CORBA::Exception *_ex = _req->env()->exception();
  /* Following is from MICO version, needs to be ported to omniORB
  CORBA::UnknownUserException *_uuex = 
     CORBA::UnknownUserException::_downcast( _ex );
  if(_uuex) {
    mico_throw( CORBA::UNKNOWN() );
  } else {
    mico_throw( *_ex );
  }
  */
}@}

@d Get the return value from the first half
@{_req->return_value() >>= result[0];@}

@d Output first half of result from result[0], second half from result[1]
@{cout << "The result: " << endl;
for (j = 0; j < 2; ++j) {
  for (int k = 0; k < result[j]->length(); ++k)
    cout << (*result[j])[k] << " ";
}@}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Running the servers and client}

When the client and two instances of the server are run as described
in the second section, the output by the client is as follows:

@d Client output
@{D:\dsc\omniorb\examples\matrix2>client matrix1 matrix2
Initializing...
j = 1
j = 2
Computing the matrix-vector product...
The result:
570 1770 2970 4170 5370 6570 7770 8970 10170 11370 12570 13770 14970 
16170 17370 18570 19770 20970 22170 23370@}

(The last line was manually broken to fit within the margins.)

The output by the servers is

@d Server 1 output
@{D:\dsc\omniorb\examples\matrix2>server matrix1
Initializing...
block_product(0, 10, c) start
block_product(0, 10, c) end@}

@d Server 2 output
@{D:\dsc\omniorb\examples\matrix2>server matrix2
Initializing...
block_product(10, 20, c) start
block_product(10, 20, c) end@}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{thebibliography}{MMM83}

\bibitem[Briggs]{Briggs} P.~Briggs, \emph{Nuweb, a simple literate
programming tool}, Version 0.87, 1989.

\bibitem[Knuth84]{Knuth:literate} D.E.~Knuth, Literate programming.
{\em Computer Journal} 27 (1984), 97--111.

\end{thebibliography}

\newpage
\appendix

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Name binding auxiliary functions}

The functions \code{bindObjectToName} and \code{getObjectReference},
used in the server and client respectively, are used to access the
Naming Service.  These functions, which are provided in the auxiliary
file \code{namebind.cpp}, are based on the Echo example that comes
with OmniORB.

@d Include auxiliary file to help with using the Naming Service
@{#include "namebind.cpp"
@}

@O namebind.cpp -d
@{static CORBA::Boolean
  bindObjectToName(CORBA::ORB_ptr orb, CORBA::Object_ptr obj, 
                   const char* context, const char* name)
{
  CosNaming::NamingContext_var rootContext;
  
  try {
    // Obtain a reference to the root context of the Name service:
    CORBA::Object_var initServ;
    initServ = orb->resolve_initial_references("NameService");

    // Narrow the object returned by resolve_initial_references()
    // to a CosNaming::NamingContext object:
    rootContext = CosNaming::NamingContext::_narrow(initServ);
    if (CORBA::is_nil(rootContext)) 
      {
        cerr << "Failed to narrow naming context." << endl;
        return 0;
      }
  }
  catch(CORBA::ORB::InvalidName& ex) {
    cerr << "Service required is invalid [does not exist]." << endl;
    return 0;
  }

  try {
    CosNaming::Name contextName;
    contextName.length(1);
    contextName[0].id   = (const char*) context;
    contextName[0].kind = (const char*) "my_context"; 
    // Note on kind: The kind field is used to indicate the type
    // of the object. This is to avoid conventions such as that used
    // by files (name.type -- e.g. test.ps = postscript etc.)

    CosNaming::NamingContext_var testContext;
    try {
      // Bind the context to root, and assign testContext to it:
      testContext = rootContext->bind_new_context(contextName);
    }
    catch(CosNaming::NamingContext::AlreadyBound& ex) {
      // If the context already exists, this exception will be raised.
      // In this case, just resolve the name and assign testContext
      // to the object returned:
      CORBA::Object_var tmpobj;
      tmpobj = rootContext->resolve(contextName);
      testContext = CosNaming::NamingContext::_narrow(tmpobj);
      if (CORBA::is_nil(testContext)) {
        cerr << "Failed to narrow naming context." << endl;
        return 0;
      }
    } 

    // Bind the object (obj) to testContext, naming it FrontOffice;
    CosNaming::Name objectName;
    objectName.length(1);
    objectName[0].id   = (const char*) name; 
    objectName[0].kind = (const char*) "Object"; 


    // Bind obj with name objectName to the testContext:
    try {
      testContext->bind(objectName,obj);
    }
    catch(CosNaming::NamingContext::AlreadyBound& ex) {
      testContext->rebind(objectName,obj);
    }
    // Note: Using rebind() will overwrite any Object previously bound 
    //       to /test/FrontOffice with obj.
    //       Alternatively, bind() can be used, which will raise a
    //       CosNaming::NamingContext::AlreadyBound exception if the name
    //       supplied is already bound to an object.

    // Amendment: When using OrbixNames, it is necessary to first try bind
    // and then rebind, as rebind on its own will throw a NotFoundexception if
    // the Name has not already been bound. [This is incorrect behaviour -
    // it should just bind].
  }
  catch (CORBA::COMM_FAILURE& ex) {
    cerr << "Caught system exception COMM_FAILURE, unable to contact the "
         << "naming service." << endl;
    return 0;
  }
  catch (omniORB::fatalException& ex) {
    throw;
  }
  catch (...) {
    cerr << "Caught a system exception while using the naming service."
         << endl;
    return 0;
  }
  return 1;
}
@}

@O namebind.cpp -d
@{static 
CORBA::Object_ptr
  getObjectReference(CORBA::ORB_ptr orb, const char* context, 
                     const char* name)
{
  CosNaming::NamingContext_var rootContext;
  
  try {
    // Obtain a reference to the root context of the Name service:
    CORBA::Object_var initServ;
    initServ = orb->resolve_initial_references("NameService");

    // Narrow the object returned by resolve_initial_references()
    // to a CosNaming::NamingContext object:
    rootContext = CosNaming::NamingContext::_narrow(initServ);
    if (CORBA::is_nil(rootContext)) 
      {
        cerr << "Failed to narrow naming context." << endl;
        return CORBA::Object::_nil();
      }
  }
  catch(CORBA::ORB::InvalidName& ex) {
    cerr << "Service required is invalid [does not exist]." << endl;
    return CORBA::Object::_nil();
  }

  CosNaming::Name fullName;
  fullName.length(2);

  fullName[0].id   = (const char*) context;
  fullName[0].kind = (const char*) "my_context"; 
  fullName[1].id   = (const char*) name;
  fullName[1].kind = (const char*) "Object";
  // Note on kind: The kind field is used to indicate the type
  // of the object. This is to avoid conventions such as that used
  // by files (name.type -- e.g. test.ps = postscript etc.)

  
  CORBA::Object_ptr obj;
  try {
    // Resolve the name to an object reference, and assign the reference 
    // returned to a CORBA::Object:
    obj = rootContext->resolve(fullName);
  }
  catch(CosNaming::NamingContext::NotFound& ex)
    {
      // This exception is thrown if any of the components of the
      // path [contexts or the object] aren't found:
      cerr << "Context not found." << endl;
      return CORBA::Object::_nil();
    }
  catch (CORBA::COMM_FAILURE& ex) {
    cerr << "Caught system exception COMM_FAILURE, unable to contact the "
         << "naming service." << endl;
    return CORBA::Object::_nil();
  }
  catch(omniORB::fatalException& ex) {
    throw;
  }
  catch (...) {
    cerr << "Caught a system exception while using the naming service."
         << endl;
    return CORBA::Object::_nil();
  }
  return obj;
}
@}

\section{A Makefile}

This Makefile is for use with the Microsoft Visual C++ compiler.  Note
the \code{IDLFLAGS} variable, which defines the flags necessary to
cause the omniORB IDL compiler to include certain type information
needed for DII calls.  These are specific to omniORB.  Some
of the file paths used in other variables are specific to the 
way omniORB is installed.

@O Makefile -t
@{# Filename   : Makefile
# Adapted from one written by : Stephen Cerniglia
# Creation Date : February 21, 1999

# Compiler Macros
IDL	 = omniidl2
IDLFLAGS = -a -h .h -s Skel.cpp 
CPP      = cl.exe
OMNIDEFS = /D__WIN32__ /D__x86__ /D__NT__ /D__OSVERSION__=4
CPPFLAGS = /c /O2 /MD /GX  @$(OMNIDEFS)
LD       = link.exe
#LDFLAGS  = -nologo
#LDFLAGS  = /nologo /debug /subsystem:console /incremental:no

# Project Macros
PROJECT = matrix
SERVER  = server
CLIENT  = client
LIBDIR  = d:\dsc\omniorb\omniorb_2.7.0\lib\x86_win32
OMNILIBS = omniDynamic270_rt.lib omniORB270_rt.lib \
   omnithread2_rt.lib wsock32.lib advapi32.lib \
   -libpath:d:\dsc\omniorb\omniORB_2.7.0\lib\x86_win32
INCLDIR = /ID:\dsc\omniorb\OmniORB_2.7.0\include


all: idl both server client

both: nuweb
	@$(CPP) @$(INCLDIR) @$(CPPFLAGS) @$(PROJECT)Skel.cpp \
              /Fo@$(PROJECT).obj
	@$(CPP) @$(INCLDIR) @$(CPPFLAGS) @$(PROJECT)DynSkel.cpp \
              /Fo@$(PROJECT)Dyn.obj

server: nuweb
	@$(CPP) @$(INCLDIR) @$(CPPFLAGS) @$(SERVER).cpp \
              /Fo@$(SERVER).obj
	@$(LD) @$(LDFLAGS) -out:@$(SERVER).exe @$(OMNILIBS) \
              @$(PROJECT).obj @$(SERVER).obj

client: nuweb
	@$(CPP) @$(INCLDIR) @$(CPPFLAGS) @$(CLIENT).cpp \
              /Fo@$(CLIENT).obj
	@$(LD) @$(LDFLAGS) -out:@$(CLIENT).exe @$(OMNILIBS) \
              @$(PROJECT).obj @$(PROJECT)Dyn.obj @$(CLIENT).obj

idl: nuweb
	@$(IDL) @$(IDLFLAGS) @$(PROJECT).idl

zip: nuweb
	zip all *.cpp *.h *.idl Makefile

clean:
	del *.obj
	del *.exe
	del @$(PROJECT)Skel.cpp
	del @$(PROJECT).h
	del *.pdb

nuweb: matrix.w
	nuweb matrix

doc: nuweb
	latex matrix
	nuweb matrix
	latex matrix
	dvips -o matrix.ps matrix
@}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Hints for using Nuweb}
\label{nuweb:hints}

The Nuweb tool used in this exercise is a minor modification to
Brigg's \cite{Briggs} original tool, or actually to a version
previously modified by Ramsdell and Mengel.  I made additional small
changes in terminology in the \LaTeX\ file the tool produces: ``part''
is used in place of ``scrap'' and ``definition'' in place of
``macro.''  This version, called Nuweb 0.91, is available from
\emph{http://www.cs.rpi.edu/\~{}musser/gp/}. The only differences are
in the \texttt{.tex} documentation file; the new version does not
differ from previous versions in the way it produces code files from
Nuweb source files.

There are a few points about Nuweb that could trip you up if you
aren't aware of them:\begin{enumerate}

 \item Nuweb normally replaces tab characters with eight blanks.  You
can have it retain the tabs by putting a flag, {\tt -t}, on 
the Nuweb \texttt{@@o} (or \texttt{@@O}) command that outputs the
file.  If you keep your makefiles in a Nuweb document, as in the
preceding appendix, be sure to use this flag since the tabs are
essential to the correct interpretation of the makefile by the
\texttt{make} utility.  Thus the command to output the makefile looks
like this:
\begin{verbatim}
   @@O Makefile -t
\end{verbatim}

 \item Another useful flag on Nuweb file output commands is
\texttt{-d}.  This causes \verb|#line| directives to be inserted in
the file, which are used by compilers, editors, debuggers, and other
tools to relate error messages back to lines in the original source
file (the Nuweb \texttt{.w} file in this case) rather than lines in
the file the tool is processing.  Using this feature makes it a lot
easier to make corrections directly to the Nuweb source file, which is
crucial to the literate programming approach---you shouldn't be even
looking at, much less editing, the code files or \texttt{.tex} file
produced by Nuweb.

 \item That said, there seems to be a problem with the omniORB IDL compiler
(\texttt{omniidl2}) getting a little confused by line directives in
\texttt{.idl} files.  It understands them to the extent of correctly
emitting error messages in terms of the Nuweb source file, but it
somehow fails to produce the correct \texttt{.h} file (containing C++
stub code).  For this reason, you must \emph{not} use the \texttt{-d}
flag when outputting IDL files for input to \texttt{omniidl2}.  (Or
you could use the flag initially while working out any syntax errors
in your IDL file, then remove it.)

This problem is not likely to appear when using other CORBA IDL
compilers, but I haven't yet tried it with any others.

\end{enumerate}

\end{document}





