[comp.sources.misc] v06i043: lookup -- look up commands in table

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (03/05/89)

Posting-number: Volume 6, Issue 43
Submitted-by: rae@b.UUCP (Reid Ellis)
Archive-name: lookup.c++


The following shar implements a lookup table in C++.  Two alternate
algorithms are presented, binary and linear search, in the files lookup.c
and lookup2.c, respectively.  See the README file for more details.  Don't
worry -- there's an "exit" before my .signature.

					Reid

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile README lookup.c lookup.h lookup2.c test.c
#   testcmd.c testhelp.c
# Wrapped by rae@geaclib on Tue Feb  7 05:37:45 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(419 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
XCC = CC -c -I/usr/include/CC
XLD = CC
X
X.c.o:
X	$(CC) $(CFLAGS) $<
X
X# lookup2 does a linear search -- good for unsorted table
X# lookup  does a binary search -- good for a sorted table
XOBJ = test.o lookup2.o testcmd.o testhelp.o
X#OBJ = test.o lookup.o testcmd.o testhelp.o
XTARG = tt
X
X$(TARG): $(OBJ)
X	$(LD) -o $(TARG) $(OBJ) $(LDFLAGS)
X	@strip $(TARG)
X
Xlookup.o lookup2.o : lookup.h
Xtest.o : lookup.h
Xtesthelp.o : lookup.h
END_OF_Makefile
if test 419 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(2827 characters\)
sed "s/^X//" >README <<'END_OF_README'
XDESCRIPTION
X
XUsing the files lookup.c [or lookup2.c] and lookup.h, you can easily maintain
Xa command lookup table.  The table must be of type "entry *", and you can see
Xin the example code [test.c, testcmd.c, testhelp.c] that I used an aggreagate
Xarray of the form:
X
Xentry list[] = { ... };
X
XThe first "entry" in the list must have a count and a pointer to an error
Xfunction.  If you have no error function, create a null function, which
Xdoes nothing, and use a pointer to it.  Since the number occupies the
Xposition of a char*, you have to cast it to such.  i.e., if you have
X#define'd NUM to the number of elements, you would say
X
Xentry list[] = {
X  (char *)NUM, &err,
X  ...
X  };
X
Xwhere err is the error-handling function.  err would be called when a string
Xnot in the list is referenced.
X
XNow, the list is not the object that does the work.  Another object, of class
X"lookup" is initialized with a pointer to an "entry *".  e.g., given the
Xabove example, you could say:
X
Xlookup cmd(list);
X
XThereafter, to retrieve the function pointer desired, you simply reference
X"cmd" with a string:
X
Xint (*proc)(const char *);
Xproc=cmd["query"];
X
XThis would cause "proc" to point to the function associated with the
Xword "query", if any; otherwise, it would point to the error function,
Xwhich we designated above to be "err".  Now you can happily dereference the
Xpointer and pass it a "const char *":
X
Xproc("testing");
X
XNow note that you can do both of these at once:
X
Xcmd["query"]("Testing");
X
XIsn't that special?
X
XThe only difference between lookup.c and lookup2.c is the implementation.
XEach gives you an advantage.  If your list of names is sorted, use
Xlookup.c since it uses a binary search.  Obviously, if they are not
Xin any order, a binary search will not work, so lookup2.c just does
Xa linear search.  This is the only difference between the two.
X
X
XARGUMENT TYPE
X
XAll of the function pointers in the "entry" list must take the same
Xargument[s], if any, and return the same value type.  I haven't built
Xany elegant macros, but there are #defines in "lookup.h" to handle both
Xof these problems in a less than elegant manner.
X
XThe two macros ARG_TYPE and RET_TYPE define the argument type and value
Xvalue returned by the function pointers.  Just change these to handle whatever
Xtypes you like.
X
X
XCAVEATS
X
XThis is one of my first attempts at C++;  I hope you find it useful.  If
Xyou make any major modifications, or even more importantly, if you find any
Xbugs, please let me know so I can fix my source and post the patches for
Xone and all.
X
X
XCOPYRIGHT
X
XI the author hereby place this code in the public domain.  Make enough
Xmoney to buy Canada and hire me as Prime Minister!
X
X
XAUTHOR
X
XReid Ellis
X176 Brookbanks Drive
XDon Mills, Ontario
XM3A 2T5
XCANADA
X+1 416 446 1644
Xrae@geaclib.uucp if you're lucky, or else geaclib!rae@geac.uucp
END_OF_README
if test 2827 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f lookup.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"lookup.c\"
else
echo shar: Extracting \"lookup.c\" \(652 characters\)
sed "s/^X//" >lookup.c <<'END_OF_lookup.c'
X#include <stream.h>
X#include <string.h>
X#include "lookup.h"
X
Xproc lookup::operator[](const char *str)
X  {
X  int n;
X  entry
X    *upper = table + max - 1,
X    *lower = table,
X    *mid = lower + (upper-lower)/2;
X
X for(n=1; n && upper - lower > 1; )
X    {
X    n = strcmp(str, mid->name);
X    if(n == 0)
X      upper = lower = mid;
X    else
X      {
X      if(n < 0) upper = mid;
X      else      lower = mid;
X      mid = lower + (upper - lower)/2;
X      }
X    }
X
X  if(n < 0) n = strcmp(str, mid->name);
X  else if(n > 0)
X    {
X    mid = upper;
X    n = strcmp(str, mid->name);
X    }
X
X  if(n) return dflt;   // not in table
X  else  return mid->f; // found it
X  }
END_OF_lookup.c
if test 652 -ne `wc -c <lookup.c`; then
    echo shar: \"lookup.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f lookup.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"lookup.h\"
else
echo shar: Extracting \"lookup.h\" \(392 characters\)
sed "s/^X//" >lookup.h <<'END_OF_lookup.h'
X#ifndef LOOKUPH
X#define LOOKUPH
X
X#define ARG_TYPE const char *
X#define RET_TYPE int
X
Xtypedef RET_TYPE (*proc)(ARG_TYPE);
X
Xstruct entry {
X	const char *name;
X	proc f;
X	};
X
Xclass lookup {
X	entry *table;
X	proc dflt;
X	int max;
Xpublic:
X	lookup(entry *t) {
X		dflt = t->f;
X		max = int(t->name);
X		table = t + 1;
X		}
X	// This really does take a const char *
X	proc operator[](const char *);
X	};
X#endif
END_OF_lookup.h
if test 392 -ne `wc -c <lookup.h`; then
    echo shar: \"lookup.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f lookup2.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"lookup2.c\"
else
echo shar: Extracting \"lookup2.c\" \(263 characters\)
sed "s/^X//" >lookup2.c <<'END_OF_lookup2.c'
X#include <stream.h>
X#include <string.h>
X#include "lookup.h"
X
Xproc lookup::operator[](const char *str)
X  {
X  entry *t=table;
X
X  for(; t-table < max; t++)
X    if(!strcmp(t->name, str))
X      break;
X
X  if(t-table<max) return t->f;
X  else            return dflt;
X  }
END_OF_lookup2.c
if test 263 -ne `wc -c <lookup2.c`; then
    echo shar: \"lookup2.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f test.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"test.c\"
else
echo shar: Extracting \"test.c\" \(882 characters\)
sed "s/^X//" >test.c <<'END_OF_test.c'
X#include <stream.h>
X#include <string.h>
X#include "lookup.h"
X
Xextern RET_TYPE
X	a(ARG_TYPE),
X	b(ARG_TYPE),
X	c(ARG_TYPE),
X	help(ARG_TYPE),
X	oops(ARG_TYPE);
X
Xentry list[] = {
X  (char *)4, &oops,
X  "acmd", &a,
X  "bcmd", &b,
X  "c", &c,
X  "help", &help,
X  };
X
Xint main(int argc, char *argv[])
X  {
X  char *progname = argv[0];
X  lookup command(list);
X  char word[200]; // of type ARG_TYPE
X  int i;
X
X  argc--; // to shut up CC
X
X  while(cout << "% ", cin >> word && strcmp(word, "quit"))
X    {
X
X    // Note that in the next statement, we are in
X    // fact saying command[const char *](ARG_TYPE)
X    // In this case, ARG_TYPE *is* const char *, so we 
X    // are just passing the same thing to both [] and ().
X
X    if(i = command[word](word))
X      {
X      // the function returned a non-zero result
X      cerr << form("%s: %s returned %d\n", progname, word, i);
X      }
X    }
X  return 0;
X  }
END_OF_test.c
if test 882 -ne `wc -c <test.c`; then
    echo shar: \"test.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f testcmd.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"testcmd.c\"
else
echo shar: Extracting \"testcmd.c\" \(328 characters\)
sed "s/^X//" >testcmd.c <<'END_OF_testcmd.c'
X#include <stream.h>
X
Xint a(const char *s)
X  {
X  cout << form("a: %s\n", s);
X  return 0;
X  }
X
Xint b(const char *s)
X  {
X  cout << form("b: %s\n", s);
X  return 0;
X  }
X
Xint c(const char *s)
X  {
X  cout << form("c: %s\n", s);
X  return 0;
X  }
X
Xint oops(const char *s)
X  {
X  cout << form("command %s unavailable\n", s);
X  return 0;
X  }
END_OF_testcmd.c
if test 328 -ne `wc -c <testcmd.c`; then
    echo shar: \"testcmd.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f testhelp.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"testhelp.c\"
else
echo shar: Extracting \"testhelp.c\" \(341 characters\)
sed "s/^X//" >testhelp.c <<'END_OF_testhelp.c'
X#include <stream.h>
X#include "lookup.h"
X
Xextern entry list[];
X
Xint help(const char *s)
X  {
X  entry *t = list;
X
X  (void) s; // "use" s so that CC won't complain
X  cout << "Available commands:\n\n";
X  cout << "quit\t";
X
X  for(int max=1+int((t++)->name); t-list < max; t++)
X    cout << form("%s\t", t->name);
X
X  cout.put('\n');
X  return 0;
X  }
END_OF_testhelp.c
if test 341 -ne `wc -c <testhelp.c`; then
    echo shar: \"testhelp.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
-- 
Reid Ellis, geaclib!rae@geac.uucp, rae@geaclib.uucp [if you're lucky]