OSE Template Preprocessor - Copyright 1991 1992 OTC LIMITED

This preprocessor is a modified version of the COOL preprocessor made
available by Texas Instruments. Modifications which have been made are
significant enough that you should refer to this version as the OSE
Template Preprocessor. The major changes which have been made are:

  - Macro preprocessing is closer to ANSI standard. One exception is
    outlined in the TODO file.

  - #line information is being output correctly around template expansions.

  - __LINE__ and __FILE__ are being expanded correctly in template
    expansions.

  - Template classes and member function template syntax is more compliant
    with ARM standard. In particular, it is now not necessary to append
    the template argument list to the constructor and destructor functions
    and also to the name of the class when first declaring it.

  - Templated static member variable initialisers are supported.

Aspects of ARM templates which are still not supported are:

  - Automatic expansion of declarations or implementations.

  - Function templates.

  - Templated forward declarations.

  - Probably other things also.

Expansion of template declarations and implementations is similar to what
was used in the COOL preprocessor, the main difference being the names
used. In particular the revised set of #pragma statements which should be
included before any templates are defined or used is:

  #pragma defmacro template "template" delimiter=}
  #pragma defmacro COOL_DECLARE "declare" delimiter=> recursive lines
  #pragma defmacro COOL_IMPLEMENT "implement" delimiter=> recursive lines
  #pragma defmacro OSE_DECLARE "declare_once" delimiter=> recursive lines
  #pragma defmacro OSE_IMPLEMENT "implement_once" delimiter=> recursive lines
  #pragma defmacro OSE_TEMPLATE "ose_template" delimiter=> recursive lines

In the above, COOL_DECLARE is the same as the original DECLARE in the COOL
version. COOL_IMPLEMENT is the same as IMPLEMENT, and OSE_DECLARE the same
as DECLARE_ONCE. OSE_IMPLEMENT has been added and is similar to the
original DECLARE_ONCE except that it works on the implementation part of a
template. The end result being of course, that if OSE_IMPLEMENT is seen
twice by the preprocessor then only one expansion will be done.

OSE_TEMPLATE has also been added and is equivalent to having both an
OSE_DECLARE and OSE_IMPLEMENT at that point. This should be used when
wishing to expand a template for use. For example

  #include <OTC/List.hh>
  #include <OTC/String.hh>

  OSE_TEMPLATE OTC_List<OTC_String>

  main(int argc, char* argv[])
  {
    OTC_List<OTC_String> args;
    for (int i=0; i<argc; i++)
      args.addLast(argv[i]);
    OTC_Iterator<OTC_String> iter(args.items());
    for (iter.reset(); iter.isValid(); iter.next())
      cout << iter.item() << endl;
   
    return 0;
  }

Note that since this will always expand both the declaration and
implementation of a class, you need to make sure that the template
preprocessor only sees the implementation of the templates when you want
instantiations to be done, ie. when compiling the main() routine.
If this isn't done then you would end up with template instantiations
in very object file which saw a particular OSE_TEMPLATE statement.

A simple way of controlling this is to split the implementation part of
your template off into a seperate file and for that to be conditionally
included at the end of the template class header file. This is okay as the
AT&T C++ 3.0 compiler expects the template implementation to be in a
seperate file anyway. Thus you might have the following:

--- list.hh ---

  #ifndef LIST_HH
  #define LIST_HH

  template<class T>
  class OTC_List { ... };

  #ifdef EXPAND_TEMPLATES
  #include "list.c"
  #endif

  #endif

--- list.c ---

  #ifndef LIST_CC
  #define LIST_CC

  template<class T>
  OTC_List<T>::OTC_List() {}

  #endif

With this in place, it is then a simple matter of defining the symbol
EXPAND_TEMPLATES when compiling files including main() routines.

The ability to conditionally compile in the implementation part of a
template is also useful when generating dependencies as it will result
in a dependency on the seperate implementation file.

Note that if using a C++ compiler which has real templates then you will
want to hide the various bits and pieces specific to the OSE Template
Preprocessor. To facilitate this, the symbol __OSE_TEMPLATES__ is
automatically defined by the preprocessor. This we can say:

  #ifdef __OSE_TEMPLATES__
  #pragma defmacro template "template" delimiter=}
  #pragma defmacro COOL_DECLARE "declare" delimiter=> recursive lines
  #pragma defmacro COOL_IMPLEMENT "implement" delimiter=> recursive lines
  #pragma defmacro OSE_DECLARE "declare_once" delimiter=> recursive lines
  #pragma defmacro OSE_IMPLEMENT "implement_once" delimiter=> recursive lines
  #pragma defmacro OSE_TEMPLATE "ose_template" delimiter=> recursive lines
  #endif

and

  #ifdef __OSE_TEMPLATES__
  OSE_TEMPLATE OTC_List<OTC_String>
  #endif

Note that it is not sufficient to do something like

  #ifdef __OSE_TEMPLATES__
  #pragma defmacro OSE_TEMPLATE "ose_template" delimiter=> recursive lines
  #else
  #define OSE_TEMPLATE name2(/,/)
  #endif

as in some C++ compilers (GNU C++ in particular) the preprocessor they use
does not pass on comments and the actual compiler doesn't know what
comments are and so will generate an error. With the macro hack above the
expansion is such that the code gets passed onto the compiler as a comment
which subsequently stops. So although this would work for AT&T based
compilers it will not work on the GNU C++ compiler. Thus the only safe way
is to have the conditional at the point where you want the template
expanded.

The __OSE_TEMPLATE__ check would also be used in the following situation
for which the syntax is particular to the preprocessor and is not part
of the ARM standard for templates.

--- list.hh ---

  #ifdef __OSE_TEMPLATES__
  template<class T> OTC_List
  {
    OSE_DECLARE OTC_Collection<T>
  }
  #endif

  template<class T>
  class OTC_List : public OTC_Collection<T> {};

--- list.c ---

  #ifdef __OSE_TEMPLATES__
  template<class T> OTC_List
  {
    OSE_IMPLEMENT OTC_Collection<T>
  }
  #endif

The use of OSE_DECLARE here is to ensure that the class Collection is
expanded for the particular type being instantiated over before the actual
List class is. This is necessary because List derives from Collection.
The OSE_IMPLEMENT is used to ensure that the implementation of Collection
is always expanded when the implementation for List is expanded. Since
this is all special to the OSE Template Preprocessor then we would
do the check against __OSE_TEMPLATES__.

Note that if you use Sniff you should stick a semicolon at the end of any
OSE_DECLARE, OSE_IMPLEMENT and OSE_TEMPLATE line so that it can recover
enough to keep parsing the file. You should also append a semicolon after
the closing brace of the preprocessor stuff for expanding base classes etc.
For example you would write:

  #ifdef __OSE_TEMPLATES__
  OSE_TEMPLATE OTC_List<int>;
  #endif

and

  #ifdef __OSE_TEMPLATES__
  template<class T> OTC_List
  {
    OSE_IMPLEMENT OTC_Collection<T>;
  };
  #endif

If you have any problems with or questions about the OSE Template
Preprocessor then email can be sent to:

  ose@research.otc.com.au

Note, if you do not have the full OSE distribution then you will need to
use Makefile.TI to build the preprocessor, ie. run

  make -f Makefile.TI

At any point in time it is unknown whether the TI makefile will still work
as I don't update it. Thus you may have to make changes to it to get it to
work.
