AUTOMATING COLLECTIONS       

Frequently an application organizes data and objects in
collections of various types, such as arrays, containers,
or linked lists, or it utilizes such capabilites inherent in
the operating platform.  Collections generally expose an
iterator, a counter, a random-access function, and
optionally methods for managing the collection. Exposing
collections for automation generally involves three steps:

  1. The collection is exposed as a property which returns
     an object.

  2. A C++ class is created, or enhanced, to support the
     collection methods.

  3. An iterator is defined to provide code to iterate
     through the collection.


The iterator is internally exposed to the script controller
as a property having the reserved name "_NewEnum" which
returns an object supporting the IENUMVariant interface.
This interface contains methods to perform iteration.  With
VisualBasic for Applications, the following syntax is used
for iteration:

  For Each Thing In Owner.Bunch  ("Thing" is an arbitrary 
                                   iterator name)
    Thing.Member......           (can access methods and 
                                   properties)
  Next Thing                     (loops through all items 
                                   in collection)

Note that the server can return any data type for items,
values or objects.


CREATING C++ CLASSES SPECIFICALLY FOR AUTOMATION

Frequently C++ classes do not already exist to encapsulate
items to be exposed for automation, such as collections,
simple structures, and system objects represented by
handles. The only C++ methods required are a constructor
and support methods for the exposed automation. Instances
of these classes are exposed as properties of their parent
classes and are constructed as the properties are retrieved
(as opposed to returning references to existing C++
instances). The constructor must accept a single argument
from the parent class: member data, result of an accessor
function, or the object's this pointer. The parent class
must supply an AUTOxxx macro that supplies this constructor
argument as a data member or function, and declares its
type with the templatized pointer class
TAutoObjectByVal<T>, where T is the new automated C++
class. TAutoObjectByVal<T> causes an instance of T to be
constructed that persists until all external references to
that instance are released (when the exposed object goes
out of scope in the automation controller).  Examples of
the macros used within the DECLARE_AUTOCLASS and
DEFINE_AUTOCLASS sections are given below:

  AUTODATARO(Documents, DocList, TAutoObjectByVal<TDocumentList>,)
  // DocList is a TDocument*, head of a linked list of.
  // type TDocument. TDocumentList is a new class, constructor: 
  // TDocumentList(TDocument*)
  EXPOSE_PROPRO(Documents, TDocumentList, "Documents", "Doc Collection", 270)

  AUTOTHIS(Views, TAutoObjectByVal<TViewList>,)
  // TViewList is a new class, constructor: TView(TDocument* owner)
  // In this case the parent's this pointer is passed to the constuctor
  EXPOSE_PROPRO(Views, TViewList, "Views", "View Collection", 240)

  AUTOFUNC0(Buttons, GetWindow(), TAutoObjectByVal<TCalcButtons>, )
  // GetWindow() gets the window handle of the parent window of 
  // the buttons. TCalcButtons is a new class, constructor: 
  // TCalcButtons(HWND parent)
  EXPOSE_PROPRO(Buttons, TCalcButtons, "Buttons", Button Collection", 170)

  AUTODATARO(MyArray, Elem, TAutoObjectByVal<TMyArray>,)
  // Elem is an array of shorts, defined as short Elem[COUNT]
  // TMyArray is a new class, constructor: TMyArray(short* array)
  EXPOSE_PROPRO(MyArray, TMyArray, "Array", "Array as collection", 110)


COLLECTION CLASSES

A C++ class must be defined to host the automation
declarations along with the supporting C++ methods.  For
some collections, a C++ class might already exist. Otherwise
one must be defined to represent a collection object. 


EXPOSING COLLECTION OBJECTS

EXPOSING ITERATORS

Iterators are defined for automation using the AUTOITERATOR
macro, which defines the iteration algorithm for the
enclosing collection class. No internal name is supplied,
as only one iterator may exist within a class.  The macro
has five arguments, each representing a code fragment,
ordered as in a "for" loop.

  1. Declaration of state variables,      e.g. int Index
  2. Loop initializer assigments,         e.g. Index = 0
  3. Loop entry test boolean expression,  e.g. Index < This->Total
  4. Loop iteration statements,           e.g. Index++
  5. Current element access expression,   e.g. (This->Array)[Index]

Commas cannot be used unless inside parentheses. Semicolons
can be used to separate multiple statements, but cannot 
be used to end a macro argument.  In common with automated
methods, "This" is defined to be the "this" pointer of the
enclosing C++ class, in this case the collection itself.

The AUTOITERATOR macro generates a nested class definition.
For complex iterators, this class can be specified directly
in C++ as shown below:

  class TIterator : public TAutoIterator {
    public:
      ThisClass* This;
      /* declare state variables here as members */
      void Init() {/* loop initialization function body */}
      bool Test() {/* loop entry test function body */}
      void Step() {/* loop iteration function body;}
      void Return(TAutoVal& v) {/* current element return: v = expr */}
      TIterator* Copy() {return new TIterator(*this);}
      TIterator(ThisClass* obj, TServedObject& owner)
                    : This(obj), TAutoIterator(owner) {}
      static TAutoIterator* Build(ObjectPtr obj, TServedObject& owner)
      { return new TIterator((ThisClass*)obj, owner); }
  }; friend class TIterator;

Iterators are exposed as properties using the
EXPOSE_ITERATOR macro. Note that no internal or external
names are supplied (the external name is internally
hard-wired as "_NewEnum"). The automation type describes the
type of the items returned from the iterator, in the same
manner as a function return.

  AUTOITERATOR(int Index, Index=0, Index<COUNT, Index++, 
  	           (This->Array)[Index])
  // Array is a member array of shorts for which an iterator 
  // is defined. It will exposed as a "_NewEnum" property which 
  // returns an OLE enumerator.
  EXPOSE_ITERATOR(TAutoShort, "Array Iterator", HC_ARRAY_ITERATOR)

In addition to exposing an iterator, a collection class by
convention exposes a "Count" method to return the number of
items in the collections, an "Index" method to randomly
access an element of the collection, and optionally,
methods to externally manage the collection, such as add and
delete.

____________________________________________________________


         ----example code from AutoCalc------

class TCalcButtons {
  public:
    TCalcButtons(HWND window) : HWnd(window) {}
    short GetCount() { return IDC_LASTID - IDC_FIRSTID; }
    HWND HWnd;

  DECLARE_AUTOCLASS(TCalcButtons)
    AUTOFUNC0 (Count, GetCount, short,)
    AUTOITERATOR(int Id, Id = IDC_FIRSTID+1, 
	  Id <= IDC_LASTID, Id++,
      TAutoObjectByVal<TCalcButton>(::GetDlgItem(This->HWnd,Id)))
};

DEFINE_AUTOCLASS(TCalcButtons)

  EXPOSE_PROPRO(Count, TAutoLong, "!Count","Button Count", 
  	HC_TCALCBUTTONS_COUNT)
  EXPOSE_ITERATOR(TCalcButton, "Button Iterator", 
    HC_TCALCBUTTONS_ITERATOR)
  EXPOSE_METHOD_ID(0, Item, TCalcButton,"!Item",  
    "Button Collection Item", 0)
    REQUIRED_ARG(TAutoShort, "!Index")

END_AUTOCLASS(TCalcButtons, "ButtonCollection", 
	"Button Collection", HC_TCALCBUTTONS)


