//
// Copyright (C) 1991 Texas Instruments Incorporated.
//
// Permission is granted to any individual or institution to use, copy, modify,
// and distribute this software, provided that this complete copyright and
// permission notice is maintained, intact, in all copies and supporting
// documentation.
//
// Texas Instruments Incorporated provides this software "as is" without
// express or implied warranty.
//
// Created: LGO 05/19/89 -- Initial design and implementation
// Updated: MBN 08/23/89 -- Include Exception class if requested
// Updated: MBN 09/18/89 -- Converted package names to uppercase
// Updated: LGO 10/12/89 -- Cfront 2.0 compatibility
// Updated: MJF 07/31/90 -- Added terse print support
// Updated: MJF 08/03/90 -- Added map_over_slots support
//
// This header  file includes only part  of the information  needed to create a
// generic class.  The generic class definition is split out into this seperate
// file  to  facilitate  the bootstrapping   process required  by  the circular
// references  in the class  hierarchy (e.g. Symbol  uses Hash_Table which uses
// Base_Hash_Table which uses Generic, and Generic uses Symbol).
//
// The Generic class adds run-time type checking capabilities to any class that
// uses  it as a  base class.  Every  superclass  of Generic must implement the
// following methods:
//
//  virtual Symbol** type_list();
//	// This is a protected virtual member function that returns a NULL
//	// terminated array whose first element is a pointer to a symbol.
//	// The rest of the array elements are pointers to the base class's
//	// type_lists.
//
//  virtual Boolean map_over_slots(Slot_Mapper procedure, void* rock = NULL);
//	// Access all slots.  Calls a function for each slot in an object.
//
//  virtual void print_self(ostream&); 
//      // protected virtual version of: ostream << this
//
#ifndef GEN_CLASSH		// If we have not yet defined the Generic class
#define GEN_CLASSH		// Indicate class Generic has been included

#ifndef GENERIC_TYPECHECK	// Default for Implementing runtime type checks
#define GENERIC_TYPECHECK 1
#endif

#ifndef ERROR_CHECKING				// If no symbol defined yet
#if GENERIC_TYPECHECK
#define ERROR_CHECKING 1			// If symbols available
#else
#define ERROR_CHECKING 0
#endif						// End #ifdef GENERIC_TYPECHECK
#endif						// End #ifndef ERROR_CHECKING

//
// Cfront 1.2 compatability hacks
//
#ifdef __cplusplus
// used to indicate constant member functions
#define CONST const
#else
#define CONST
#endif

#ifndef STREAMH			// If the Stream support not yet defined,
#if defined(DOS) || defined(M_XENIX)
#include <stream.hxx>           // include the Stream class header file
#else
#include <stream.h>		// include the Stream class header file
#endif
#define STREAMH
#endif

#ifndef MISCELANEOUSH		// If we have not included this file
#include <cool/misc.h>		// Include miscelaneous useful definitions
#endif

class Symbol;
class Package;
class Generic;

#ifndef DEFPACKAGEH				// If no defpackage macro
#include <cool/defpackage.h>				// Include header file
#endif

//  map_over_slots calls this function for each slot in an object.
//  "rock" is a pointer to arbitrary data for optional use by the mapper.
typedef Boolean (*Slot_Mapper)
     (Generic* obj, char* slot, void* value, char* type, void* rock);

class Generic {
protected:
  Generic(){};			// Abstract class's have Protected constructors

#if GENERIC_TYPECHECK

  // type_list is a NULL terminated array whose first element is a pointer to a
  // symbol.  The rest of the array elements are pointers to the base class's
  // typelists.
  virtual Symbol** type_list() CONST;

  // Called when the first instance of a class is created
  /* there's no way (I know of) to implement this...
    virtual void initialize_class();
  */
  virtual void print_self(ostream&) CONST;	// print self on ostream
#endif // GENERIC_TYPECHECK

public:
#if GENERIC_TYPECHECK
// NOTE: virtual destructors don't work in cfront 1.2
#ifdef __cplusplus
 virtual
#endif
  ~Generic();					// Virtual destructor
  inline Symbol* type_of() CONST;		// Return the type symbol

  // Type checking predicate.  This knows about the base classes.
  Boolean is_type_of(Symbol* type) CONST;       // Type checking predicate

  // Support an efficient typecase macro using select.
  // A null-terminated list of Symbol*'s is passed in, and the
  // index of the matching type symbol is returned, or -1 if none match.
  int select_type_of(const Symbol** type_vector) CONST;

  // Access all slots.  Calls a function for each slot in an object.
  // "rock" is a pointer to arbitrary data for optional use by the mapper.
  virtual Boolean map_over_slots(Slot_Mapper procedure, void* rock = NULL);

  virtual void describe(ostream&);	// Display all slots in some "raw" format    
  friend ostream& operator<< (ostream&, const Generic&); // Overload output
#endif // GENERIC_TYPECHECK
  friend ostream& operator<< (ostream&, const Generic*); // Overload output

  void print(ostream&);				// terse print

};

#if GENERIC_TYPECHECK

inline Symbol* Generic::type_of() CONST {
  return *(this->type_list());
}

extern Boolean compare_types( Symbol** typelist, Symbol* type);
inline Boolean Generic::is_type_of(Symbol* type) CONST {
  return compare_types(this->type_list(), type);
}

extern int compare_multiple_types(Symbol** type_list, const Symbol** test_types);
inline int Generic::select_type_of(const Symbol** test_types) CONST {
  return compare_multiple_types(this->type_list(), test_types);
}
#endif // GENERIC_TYPECHECK

//
// DECLARE_Generic
//	A call to this is inserted by the class defmacro at the end of
//	every Generic class specification.
//
MACRO DECLARE_Generic(class, REST: bases) {
#if GENERIC_TYPECHECK
  WHEN_GENERIC_CLASS(class, bases) {
    protected:
      virtual Symbol** type_list() CONST;
      virtual void print_self(ostream&) CONST;
      virtual Boolean map_over_slots (Slot_Mapper procedure, void* rock = 0);
  }
#endif
}
//
// IMPLEMENT_Generic
//	A call to IMPLEMENT_Generic is inserted by the class defmacro
//      after every Generic class.  IMPLEMENT_Generic defines another
//      macro called IMPLEMENT_GENERIC_##class which is invoked from
//      IMPLEMENT_GENERIC.
//
//	This supports both runtime object typing, and map_over_slots.
//
// Note:
//	It expands the definition of the map_over_slots member function to
//      first call the map_over_slots member function of all of its base
//      classes (which inherit from Generic) and then call arg "procedure"
//      on each new slot of the class.  The "slots" body field expands to:
//      "|| (*procedure)(...) || (*procedure)(...) || ..."
//      via the generate_map_slot macro.
//
// NOTE:
//	Package needs Symbol, and Symbol needs Hash_Table and Hash_Table is
//	Generic and invokes this macro.  Because IMPLEMENT_Generic can define
//	symbols before they can be defined, this macro does nothing when
//	symbols haven't been defined.  IMPLEMENT_Generic is invoked for
//	Symbol, Base_Hash_Table, Hash_Table<Symbol, Generic>,
//      Hash_Table<Char*, Symbol> and Package in Generic.C
//
// NOTE:
//      IMPLEMENT_GENERIC is defined in misc.h, with the following definition:
//      MACRO EXPANDING IMPLEMENT_GENERIC(class) {
//      #if defined IMPLEMENT_GENERIC_##class && defined GENERIC_DEFINED
//       ONCE_ONLY(class##_Support) {
//        IMPLEMENT_GENERIC_##class()
//       }
//      #endif
//      }

MACRO IMPLEMENT_Generic(class, REST: bases, BODY: slots) {
#if GENERIC_TYPECHECK
 MACRO_IMPLEMENT_GENERIC(class) {
  WHEN_GENERIC_CLASS(class, bases) {
    GENERATE(base, bases)
     { extern Symbol* base##_types[]; }
   Symbol* class##_types[] =
        {SYM(class), GENERATE(base, bases){(Symbol*) base##_types, } NULL};
   Symbol** class##::type_list() CONST {return class##_types;}

   void class::print_self(ostream& os) CONST { os << *this; }

   Boolean class::map_over_slots (Slot_Mapper procedure, void* rock)
     { return (GENERATE(base, bases) {
 #ifdef base##_BaseClass
      base::map_over_slots (procedure, rock) ||
 #endif
}
      FALSE  slots);
     }
   }
 }
 IMPLEMENT_GENERIC(class)
#endif
}

// Create an implement macro for class
MACRO EXPANDING MACRO_IMPLEMENT_GENERIC(class, BODY: body)
{ MACRO IMPLEMENT_GENERIC_##class() {body}}

// Expand BODY when class inherits from Generic
MACRO EXPANDING WHEN_GENERIC_CLASS(class, REST: bases, BODY: body) {
#undef class##_BaseClass
#if ISSAME(Generic_BaseClass, GENERATE(b, bases),{b##_BaseClass})
 #define class##_BaseClass Generic_BaseClass
 body
#else
/* class with Base bases isn't Generic;
  GENERATE(b, bases),{b##_BaseClass}
  Generic_BaseClass : class##_BaseClass  */
#endif
}


// generate_map_slot is expanded within IMPLEMENT_map_over_slots
//
MACRO EXPANDING generate_map_slot (type, name, value) {
    || ((*procedure) (this, #name, &name, #type, rock))
}

#if GENERIC_TYPECHECK
#pragma defmacro class "class"
#pragma defmacro classmac "classmac" delimiter=)
classmac(DECLARE_Generic, inside)
classmac(IMPLEMENT_Generic, slots=generate_map_slot)
#endif						// End GENERIC_TYPECHECK


#endif						// End GENERIC_H

