            -----------------------------------------------
            High C/C++ Compiler with DirectToSOM capability
            -----------------------------------------------
							11/3/93
SOM_release_order allows "*" to skip an entry.

							10/15/93
Include metaclass override.
							
							9/15/93
Expand the som_attribute pragma.   Metaclass and callstyle pragmas.
							
							8/4/93
SOM_version and SOM_release_order can now be specified outside the
class to which they're being applied.  Edited original readme text.

							8/2/93
The compiler now allows re-releasing base class data and functions.
Edited original readme text.

                                                        7/30/93

This release of the compiler supports mapping C++ directly to SOM object
bindings.  This is a loosely organized readme file describing the state
of the compiler thus far.

1. Quick start

   The easiest way to test your C++ program in SOM mode is to use the
-Hsom switch on the compile line:

	hc test.cpp -Hsom libsom.a		(rs6000)
	hc386 test.cpp -Hsom -l\some\path\somtk.lib  	(OS/2)
	
You can then run the program.  On the RS6000, the SOM library used for
testing here was called "libsom.a"; for OS/2 it was called "somtk.lib".
You need to use the "-l" parameter for OS/2 to be sure that the library
is linked as a library; otherwise the linker binds in all the object
code of the library.  For UNIX systems this is not necessary; the
linkers there are smarter.

2. Making your C++ classes SOM classes

   Both SOM and non-SOM classes can be intermixed in a program; however,
in class hierarchy, all classes must be one or the other kind of class.
You can declare your class to be a SOM class by deriving from SOMObject.
SOMObject is declared in an included header file "som.ch".  Deriving
from SOMObject allows use of methods introduced in SOMObject in your C++
classes.  For example:
	
	#include "som.ch"
        struct s : SOMObject {
            s();
            hello() { 
            	printf("Attention!  A message from a SOM object: %s\n",msg);
            	}
            char *msg;
            };
        s::s() { msg = "Hello, world!"; }
        main () {
            s x;
            SOMClass *typeof_x = x.somGetClass();
            printf("x is a SOM object of type %s and its size is %d = %d\n",
            	typeof_x->somGetName(),
            	typeof_x->somGetInstanceSize(),
            	sizeof(x)
            	);
            x.hello();
	    printf("Static access to class object: %s\n",
	    	((SOMClass*)s::__ClassData[0])->somGetName());
            }

Notice that you can get the size of an instance of a class either
through using the class object or by using sizeof; for SOM objects,
sizeof is no longer a constant, but instead is a run-time call to the
class object.  You can also access a class object by using the ClassData
structure.  For a class s, s::__ClassData is an array of integers;
select the one you want and cast to the appropriate type.

This program prints:

	x is a SOM object of type s and its size is 8 = 8
	Attention!  A message from a SOM object: Hello, world!
	Static access to class object: s

If a class inherits from a SOM class, it too is a SOM class, and you
need not specifically say that it inherits from SOMObject.  For example:

	#include "som.ch"
        struct s : SOMObject {	// Inherit from SOMObject to make a SOM class.
            virtual void foo();
            };
        void s::foo() { printf("s::foo\n"); };
        struct d : s {		// This is a SOM class as well.
            virtual void foo();
            };
        void d::foo() { printf("d::foo\n"); };

	void what_is(s *p) {
	    printf("p is of type %s; calling foo:\n",
	    	p->somGetClass()->somGetName());
	    p->foo();
	    }
        main () {
            what_is(new d);
            what_is(new s);
            }

This program prints:

	p is of type d; calling foo:
	d::foo
	p is of type s; calling foo:
	s::foo

Rather then editing class declarations to derive from SOMObject, you can
instead turn on toggle SOM_bindings.  All class declarations in the
scope of this toggle are taken as SOM classes.  For example:

	pragma on(SOM_bindings);
        struct s {
            s();
            hello() { 
            	printf("Attention!  A message from a SOM object: %s\n",msg);
            	}
            char *msg;
            };
        s::s() { msg = "Hello, world!"; }
        main () {
            s x;
            printf("x is a SOM object of size %d\n",sizeof(x));
            x.hello();
            }

This program prints:

	x is a SOM object of size 8
	Attention!  A message from a SOM object: Hello, world!
	
Using SOM_bindings is a little riskier then explicitly inheriting,
because you might unintentially make a class a SOM class that you
preferred to keep as native C++.  For example, many class libraries
pre-declare C++'s class iostream even before including iostream.h:

	pragma on(som_bindings);
	class iostream;
	class myclass {	// I want this to be SOM.
	    iostream *p;
	    };
	pragma pop(som_bindings);	// back to off.
	#include <iostream.h>
	
You have now made iostream a SOM class because its predeclaration
appeared in a context where som_bindings was on.  However, in the C++
run-time library, iostream is not a SOM class.  You will also obtain
compile-time errors because iostream inherits from non-SOM classes
istream and ostream.

The -Hsom switch makes use of the som_bindings toggle safer.  -Hsom
essentially does the following:
	
	- turn on som_bindings
	- ensure that all C and C++ header files are compiled on
	  a non-SOM basis
	  
The header files are protected by "wrapping" them with other include
files that turn off and on the som_bindings toggle.  You can see this
work, if interested, by saying -Hon=list when you compile. -Hsom will
ensure that C or C++ run-time library headers are compiled C++ native,
not SOM.  You can also cause include files from your own directories
to be similarly "wrapped" with the command-line switch -Hsomwrap=.
For example:

	hc foo.cpp -Hon=som_bindings -Hsomwrap=nonsom

Here, any include file with the string "nonsom" in it will be wrapped
with "pragma off(som_bindings)" at the beginning and "pragma
pop(som_bindings)" at the end.  -Hsomwrap behaves just like -Hc_wrap
(see its documentation in the Progammer's Guide), except that the
wrapper files it uses are called soml.h and somr.h.

3. som.ch

   som.ch is a supplied header file that declares SOMObject and SOMClass
and some of the ancillary non-SOM structures they require.  The
declarations may clash with those in the standard SOM include files
(such as somapi.h); som.ch is not intended to work with those (yet).

4. The "seat" of a class definition.

   A SOM class X introduces a function called XNewClass that constructs
the class.  There must a single compilation unit containing this
function. The compiler chooses this compilation unit automatically for
most classes. It picks the unit containing the definition of the first
non-pure, non-inline function in the class.  Some classes, however, 
may not have such a function.  For example:

	pragma on(som_bindings);
	struct s { virtual foo() { /* foo is inline */ } };

Here, s has no non-pure, non-inline function (foo is inline). For this
class, the compiler will issue a warning that the class must be
initialized, and recommends you use the pragma SOM_define_class to do
so.  For example:

	pragma on(som_bindings);
	struct s { virtual foo() { /* foo is inline */ } };
	pragma SOM_define_class(s);

You can also turn on a toggle of the same name to force the definition
of all classes in the scope:

	pragma on(som_bindings);
	pragma on(som_define_class);
	struct s { virtual foo() { /* foo is inline */ } };

5. unions.

   Unions can never be SOM.  The reason for this is that SOM classes
must all have virtual functions, and C++ disallows unions from having
virtual functions (because you shouldn't overlay union data with a
method table!).  The compiler ignores any kind of requests to make a
union a SOM class.

6. Overriding non-C++ methods.

   The C++ compiler uses a mangling algorithm to create an external name
for any methods in a class.  Non-C++ classes -- e.g. SOMClass -- don't
use this.  To override a method in a non-C++ class you must tell the
compiler to avoid the mangling.  This is done with toggle
use_override_signature (default on).  For example:

	#include "som.ch"
	struct s : SOMObject {
    	    pragma off(use_override_signature);
    	    SOMClass* somGetClass();
    	    pragma pop(use_override_signature);
    	    };
	SOMClass* s::somGetClass() {
    	    printf("In s::somGetClass!\n");
    	    return SOMObject::somGetClass();
    	    }
	main () {
    	    s x;
    	    printf("x's class is %s\n",x.somGetClass()->somGetName());
    	    }

This program prints:

	In s::somGetClass!
	x's class is s

Here s overrides SOMObject's somGetClass function, and s::somGetClass
makes an explicit call to SOMObject's function to do its work. The
implications of this toggle with overloading in the C++ compiler haven't
been thought through carefully, so use it only to override functions
that you know exist in base class declarations.

7. inline functions

   Inline functions essentially destroy binary compability.  You can
prevent inline functions from being inlined by turning off toggle
SOM_inline:

	pragma off(som_inline);	// in your program
	-Hoff=som_inline	// on the command line

There are some indications that there a few cases in which this doesn't
work; we are investigating.  

   The compiler warns when inline subsitution is detected.  See "Turning
off warnings" below.
  
8. Class version control

   There are two forms of this pragma:

   	pragma SOM_version(major,minor) 
   	pragma SOM_version(classname; major,minor) 

   pragma SOM_version(major,minor) allows you to specify a version
number pair attached to a class X.  The pragma should appear within the
class definition.  The major and minor numbers are incorporated into the
somBuildClass data structure at the site of the class definition. They
are passed as parameters to XNewClass at any site at which the class is
used.

   The second form of the pragma can appear outside the declaration of
the class; the classname identifies the class whose version is
specified. For example,

   	pragma SOM_version(s; 1,3);
   	pragma SOM_version(Heap<char*>; 1,3);
	
9. somDataResolve [for future SOM versions]

   Currently SOM does not implement data access through thunks.  That
is, p->i = 3 make a call to somDataResolve passing it the data token
for the appropriate class.  When SOM has changed to make the data token
a thunk, turn off this toggle; the compiler will then generate an
indirect call through the data token to do the resolution.

10. Release order

   The release order of items in a class -- both functions and static
data -- can be specified with pragma som_release_order.  For example:

	struct s {
    	    s();
    	    foo();   
    	    static int a,b,c; int d;
    	    virtual goo();
    	    virtual goo(int);
    	    static static_fcn();
    	    pragma som_release_order(goo(), 
    		goo(int),static_fcn(),c,b,a,s(const s&),
    		s& operator =(const s&), foo()
    		);
    	    };
    	
Functions, if overloaded, must have the argument types specified;
otherwise a single identifier is permitted.  Note the inclusion of two
functions in the release order that were not explicitly declared by the
programmer: a copy constructor s(const s&) and the assignment operator
=.  The C++ language requires that both of those be declared.

   You can re-release base class functions or data.  You may wish to
designate such base class members with a classname:: prefix, as provided
in standard C++ syntax.  For example: 
	
	pragma som_release_order(B::foo, a, b, C::goo(int));

   Toggle print_som can be used to print the release order the compiler
assigns to a class.  You can then include the resultant release order in
your class declaration. 

   There are two forms of this pragma:

   	pragma som_release_order(list of data and functions)  
   	pragma som_release_order(classname; list of data and functions)

The first form must appear within the class definition; the second may
appear outside of it.  

To indicate a "hole" in the release order, write * instead of a function
or variable name.

WHOOPS!  -- outside the class this will fail; it will be ignored,
because the release order gets computed as soon as the class is closed.
If we wait too long we'll generate code that depends on it.

10a. Attributes

   An instance data item can be declared as a so-called attributes.  If
an attribute, access to a data item is through forced to occur through
get and set functions.  For a data item x of type T, a function T
_get_x() is used to read the item, and _set_x(T) is used to set it (if T
is an array type, T& is used instead of type T in the signature of the
functions).  (For accesses through "this", set and get are bypassed;
presumably member functions are allowed direct access.)

   Over a region of declarations where toggle som_attribute is On, the
declared instance items are treated as attributes.  Alternatively, items
can be individually named as attributes using the som_attribute pragma.
This pragma has the forms:

    pragma som_attribute(x1, ... xn) 
    pragma som_attribute(x1, ... xn; m1, m2, ... mk)

where x1, ... xn are the data items, and m1, m2 ... mk are modifiers.
A modifier is one of the following identifiers:
	nodata indirect noget noset persistent readonly

   For example:
   	struct s {
   	    pragma on(som_attribute);
   	    int a,b;
   	    pragma pop(som_attribute);
   	    int c,d,e;
   	    pragma som_attribute(d,e; nodata, indirect);
   	    };
   	// You must provide bodies for nodata attributes:
   	int global_d, global_e;
   	int &s::_get_d() { return global_d; }
   	void s::_set_d(int &x) { global_d = x; }
   	int &s::_get_e() { return global_e; }
   	void s::_set_e(int &x) { global_e = x; }

   Here, a, b, d, and e are attributes.

   The compiler declares the get and set functions.  If a release order
pragma is given, the user is responsible for naming those functions in
the release order.   If the readonly modifier is used, no set function
is declared.

    Unless the modifier nodata is given, the compiler also defines the
functions.  If nodata is given, the compiler simply provides the
declarations, and it is the user's responsibility to provide the
definitions.

   If the modifier noget is given, the compiler does not define the get function.
   If the modifier noset is given, the compiler does not define the set function.
   If the modifier indirect is given, the type returned and taken is
T& instead of T.
   If the modifier persistent is given, NOTHING HAPPENS.  Liane/Scott,
what does this modifier do?

As another example:

	pragma on(som_bindings);
	struct s {
    	    int y; 
    	    pragma som_attribute(y; nodata, noget, noset);
    	    s() { y = 1; };
    	    };
	int s::_get_y() {
    	    printf("AHA!  Somebody's peeking at y!\n");
    	    return y;
    	    }
	void s::_set_y(int i) {
    	    printf("OHO!  Somebody's assigning %d to y!\n",i);
    	    y = i;
    	    }
	main () {
    	    s z;
    	    printf("z.y=%d\n",z.y);
	    z.y = 7;
    	    printf("z.y=%d\n",z.y);
    	    }

This program prints:

	AHA!  Somebody's peeking at y!
	z.y=1
	OHO!  Somebody's assigning 7 to y!
	AHA!  Somebody's peeking at y!
	z.y=7

10b. Setting an explicit metaclass.

   You can specify an explicit metaclass for a class by using pragma
som_metaclass:

   	pragma som_metaclass(this,that);
   	pragma som_metaclass(that);
   
Class "this" will have metaclass "that" when class "this" is constructed
(or some class derived from "that", in the sole judgement of the SOM
run-time).  If just the parameter "that" is used, "this" is taken as the
currently-being-defined SOM class (thus the pragma must appear within a
class definition).

For example, you might count the number of objects created of a
particular type by replacing somRenewNoInit, which is the function that
DirectToSOM C++ compilers are obliged to call when creating an object.

	#include "som.ch"
    	struct newmeta: SOMClass {
    	    pragma off(use_override_signature);
    	    SOMObject* somRenewNoInit(void* obj);
    	    pragma pop(use_override_signature);
    	    int nobjects;
    	    newmeta() { nobjects = 0; }
    	    };
    	SOMObject *newmeta::somRenewNoInit(void *obj) {
    	    nobjects++;
    	    printf("Object #%d is allocated at %x\n",nobjects,obj);
    	    // SOMClass does the real work.
    	    SOMClass::somRenewNoInit(obj);
    	    }
	struct s : SOMObject {
	    pragma som_metaclass(newmeta);
	    s(); ~s();
    	    };
	s::s() { printf("s::s this=%x\n",this); }
	s::~s() { printf("s::~s: this=%x\n",this); }
	main () {
	    s a,b,c;
	    // Get s's metaclass and display the count:
	    newmeta *np = (newmeta*)a.somGetClass();
	    printf("A total of %d objects have been created.\n",
	    	np->nobjects);
    	    }

This program prints:    	
    	
	Object #1 is allocated at 2ff7fce0
	s::s this=2ff7fce0
	Object #2 is allocated at 2ff7fcd8
	s::s this=2ff7fcd8
	Object #3 is allocated at 2ff7fcd0
	s::s this=2ff7fcd0
	A total of 3 objects have been created.
	s::~s: this=2ff7fcd0
	s::~s: this=2ff7fcd8
	s::~s: this=2ff7fce0

Warning -- overriding methods in a metaclass is not recommended because
in the presence of multiple inheritance, SOM automatically derives new
metaclasses from the metaclasses of the base classes, and problems arise
if both metaclasses override the same method.  IBM's SOM will provide a
proper "metaclass framework" in a future release to allow safe metaclass
programming.  This example, however, was provided for illustrative
purposes.


10c. Setting an explicit call style.

   You can specify the call style for a SOM class by using pragma
som_callstyle:

   	pragma som_callstyle(this,IDL);	// or OIDL.
   	pragma som_callstyle(IDL);	// or OIDL.
   
Class "this" will have callstyle IDL or OIDL.  If the parameter "this"
is omitted, "this" is taken as the currently-being-defined SOM class
(thus the pragma must appear within a class definition).

10d. Toggle call_renew

   The compiler usually turns raw storage into a SOM object by directly
plugging in the method table.  This is extremely fast, often taking
just one instruction.  Alternatively, somRenewNoInit on the object's
class object can be called.  This is a "more legal" way of doing things,
and allows the class object to keep track of instances of the class.

   To cause somRenewNoInit to be called, turn on toggle call_renew. The
compiler will use an optimized thunk for this call, as most classes need
not record objects that are created, so that the thunk merely installs
the method table and returns.  However, for classes that need to do
other work, the thunk transfers control to the class's somRenewNoInit
method.
   
   [WE SHOULD HAVE A BETTER NAME FOR THIS.]
      
11. Optimization

   The compiler needs to do common-subexpression elimination on multiple
calls to somDataResolve and certain calls to som methods, such as
GetClassMtab.  Because this is not done yet, data access may seem very
inefficient.  Even class member functions make multiple calls so the
class object on behalf on which the function was called.  This is a
high-priority item.

12. Turning off warnings.
   
   You can turn off warnings in compilation with pragma offwarn.
For example:
	pragma offwarn(216);
turns off the "informational" message #216 that tells what classes are
SOM classes and the number of virtual, non-virtual, and static functions
the class has.  You can specify this pragma as above within your source
code, or on the command line:
	-Hpragma=offwarn(216)		(double-quote it if on UNIX)
or on the ARGS= line in your .cnf file in the bin directory:
	ARGS=-Hpragma=offwarn(216)

12b. Debugging:  __somThis.

    Currently, debuggers are unaware of the SOM-ness of objects -- they
cannot easiliy find the instance data of an object and display it
conveniently.  To assist in debugging, any class method compiled with
debugging will have declared at its beginning a variable called
__somThis.  __somThis is declared to be of the class type (which
contains just its instance data) and dynamically initialized with the
starting address of the data.  Thus, on can display a class's introduced
instance data by displaying *__somThis in the debugger.
		
13. Future directions/work:

	- A more intelligent choice of data type for the predeclared
	  __ClassData and __CClassData members.
	  
	- Allow the user to write his own XNewClass function, rather
	  than having the compiler generate it.
	  
	- The compiler should do more checks for naming conflicts, esp.
	  in the presence of off(use_override_signature);

14. IDL generation -- this section for Scott Danforth only.

   The compiler supports the ability to iterate through the class
members in order to generate IDL for the class.  The sequencing is
controlled through a printf-like format statement that controls the
iteration and allows printing information about the iterated items.

   The following is excerpted from the compiler source text and explains
the possibilities.

TO BE UPDATED

/*
   Interpret a format string that specifies iterations over a set of
   variables and functions.
   
   %if(...%)	Iterate over functions.  
   %ir(...%)	Iterate over released items.
   %id(...%)	Iterate over data.
   %im(...%)	Iterate over the members.
	Suppose the function is int (*f(int))(char *), or the data is
		static int x;
	%ddm 	-- full declaration; used mangled name; map arguments.
		-- E.g., int (*f__Fv(int))(char *)
		-- 		static int x;
	%dds 	-- full declaration; used SOM-mangled name; map arguments.
		-- E.g., int (*__f__Fv(int))(char *)
		-- 		static int x;
	%ddn	-- full declaration, normal (don't mangle).
		-- E.g., int (*f(int))(char *)
		-- 		static int x;
	%dar    -- Arguments -- (int).
	%dmn 	-- mangled name (e.g., f__Fi).  For data, no mangle.
	%dnn 	-- normal name, not mangled.
	%dsn 	-- SOM-mangled name.  For data, no mangle.
	%dac    -- access (public, private, protected).

   %?d(...%)	Do this if the iterated-over item is data.
   %?t(...%)	Do this if the iterated-over item is a type.
   %?f(...%)	Do this if the iterated-over item is a function.
   %?s(...%)	Do this if the iterated-over item is static (fcn or data).
   %?!	-- Reverse sense of test.
	Optional: ?fs, ?fn, ?fv -- static; non-virtual&non-static; virtual
   %T		The struct type being iterated over.

   Other format designations:
   %n	-- newline, followed by optional tabs (see %+).
   %t	-- tab
   %q	-- double-quote
   %s	-- space
   %+t	-- Increase tab count.  When %n occurs, \n is printed followed
		by as many tabs as the tab count.
   %-t  -- Decrease tab count.
   %Z	-- for Z anything else produces a Z.
   
 */

The format string is communicated to the compiler via the SOM_IDL_format
pragma.  If this pragma is given, AND if toggle print_som is on, the
compiler processes the IDL format for each SOM class for which the
current compilation unit is the definitional seat.

   Using a format string will allow flexibility as we consider different
ways of presenting information to IDL.  IBM will able to modify the
output of the compiler without intervention from MetaWare.  Additonal
format directives may be needed as we jointly determine.

   Given this ability, the following program illustrates an example of
programming the IDL format to print out an IDL declaration for a class
resembling that which is desired by Scott in his IDL/C++ issues paper.

pragma on(som_bindings);
pragma on(print_som);
struct s {
    s();
    foo();   
    static int a,b,c; int d;
    virtual goo();
    virtual goo(int);
    static static_fcn();
    pragma som_release_order(goo(), 
    	goo(int),static_fcn(),c,b,a,s(const s&),
    	s& operator =(const s&), foo()
    	);
    };
s::s(){}
pragma som_idl_format(
    "interface %T{%+t%n"
    	"%if(%dds;%n%)%n"
        "#ifdef __SOMIDL__%n"
        "implementation {%+t%n"
            "callstyle = oidl;%n"
            "releaseorder: %ir(%dsn, %);%n"
            "declarationorder: %im(%dsn, %);%n"
            "%if(%dsn: %dac%?fn(, nonvirtual%)%?s(, nonmethod%);%n"
            	"%tcxxdecl=%q%ddn;%q%n"
            	"%tcxxmap=%q%dnn%dar%q%n"
                "%)"
            "%id(%ddm; %?s(// data is static%) %?a([%ia(%dat,%)]%)%n%)"
            "};%-t%n"
        "#endif%n"
    	"};%-t%n"
    );     

Notice that the argument to the pragma is just one big string --
multiple adjacent strings are just concatenated in C.

This program produces the following output for the interface declaration:

interface s{
    __ct_FRC1s(const s & __dummy);
    somConstruct(void);
    int __foo_Fv(void);
    int __goo_Fv(void);
    int __goo_Fi(int);
    int __static_fcn_Fv(void);
    s & ____opasn_FRC1s(const s & __dummy);
    
    #ifdef __SOMIDL__
    implementation {
        callstyle = oidl;
        releaseorder: __goo_Fv, __goo_Fi, __static_fcn_Fv, c, b, a, __ct_FRC1s, ____opasn_FRC1s, __foo_Fv, ;
        declarationorder: somConstruct, __foo_Fv, a, b, c, d, __goo_Fi, __static_fcn_Fv, ;
        __ct_FRC1s: public;
            cxxdecl="s(const s & __dummy);"
            cxxmap="s(const s & __dummy)"
        somConstruct: public;
            cxxdecl="s(void);"
            cxxmap="s(void)"
        __foo_Fv: public, nonvirtual;
            cxxdecl="int foo(void);"
            cxxmap="foo(void)"
        __goo_Fv: public;
            cxxdecl="int goo(void);"
            cxxmap="goo(void)"
        __goo_Fi: public;
            cxxdecl="int goo(int);"
            cxxmap="goo(int)"
        __static_fcn_Fv: public, nonmethod;
            cxxdecl="int static_fcn(void);"
            cxxmap="static_fcn(void)"
        ____opasn_FRC1s: public, nonvirtual;
            cxxdecl="s & operator =(const s & __dummy);"
            cxxmap="operator =(const s & __dummy)"
        int a; // data is static
        int b; // data is static
        int c; // data is static
        int d; 
        };
    #endif
    };

