Object-Oriented Programming Using SOM and DSOM/Writing Your Own Emitter

One of the features of SOM is that it allows you to do object-oriented design and programming without restricting you to a particular programming language. In addition, SOM allows applications to access objects, regardless of the programming languages in which they were created. This means that a C++ program can use classes developed in Smalltalk and vice-versa. These characteristics are often referred to as language neutral. It is believed that this will significantly increase reuse and promote better inter-operability between programming languages.

To be language neutral, the interface for a class must be defined separately from its implementation. In SOM, the class interface is defined using SOM IDL. The IDL is then compiled by the SOM compiler to create an implementation file where the class implementation is added. To make it easier for programmers to implement SOM classes, and clients to use SOM classes, the SOM compiler can also invoke specific emitters to produce language specific bindings. Bindings are a set of macros and procedures that tailor the IDL interface to a particular programming language. For example, the C bindings allows C programs to invoke methods on SOM objects in the same way they make ordinary procedure calls. The C++ bindings allow C++ programs to invoke methods on SOM objects in the same way they invoke methods on C++ objects.

Currently, C and C++ bindings are available for the IBM and Borland compilers. Vendors of other language compilers may offer their own language bindings in the future. To help implementors write their own language bindings, SOM provides an Emitter Framework. The Emitter Framework is a collection of SOM classes that allows programmers to write their own emitters.

What is an Emitter?
We have seen a number of emitters already; for example, the Interface Repository emitter (ir), the C binding files and implementation template emitters (h, ih, c), the C++ binding files and implementation template emitters (xh, xih, xc), and the export file emitter (del). The term emitter can be thought of as the back-end output component of the SOM compiler: What this means is that programmers do not have to write their own IDL parsers if they want to understand or analyze an IDL file. Instead, they write an emitter using the classes in the Emitter Framework. The parsing is handled by the SOM compiler. The parsed information is passed to the emitter as an object. The emitter determines what information is needed for output, and writes it out accordingly.
 * The input to an emitter is information about an IDL interface. This information is produced by the SOM compiler as it parses an IDL file.
 * The output from an emitter is a file that contains information translated from an IDL interface file into a different format that is specific to the purpose of the emitter.

Developing an Emitter
The development of an emitter involves the following steps. The following sections provide details on each of the above steps.
 * 1) Run the newemit program to generate a complete working emitter.
 * 2) Customize the output template.
 * 3) Customize the emitter implementation file.
 * 4) Build the emitter.
 * 5) Invoke the emitter via the SOM compiler.

The newemit Program
The newemit program is an emitter generator that generates a complete working emitter. You can then customize the emitter to your needs. The newemit program takes two parameters: the name of the emitter class and a file stem. The file stem represents the name of your emitter. The newemit program generates the following files in your current directory: For example, the following command creates an emitter class ReportEmitter and the name of the emitter is rep. newemit ReportEmitter rep The files that are generated are rep.idl, rep.c, rep.efw, emitrep.c, emitrep.def, and Makefile.
 * .idl—The IDL definition for your new emitter. Your emitter is always derived from the SOMTEmitC class. The SOMTEmitC class provides overall control for the emitting process. The IDL specifies that the somtGenerateSections method is overridden.
 * .c* - The C implementation file for your emitter class. This file contains a default implementation for the somtGenerateSections method. (*The newemit program in the SOMobjects Developer Toolkit 2.0 generates the emitter implementation file and the emitter driver program in C. If you install CSD202 or higher, then you can choose to generate a C++ implementation file and emitter driver program.)
 * .efw—A sample output template file.
 * emit .c—An emitter driver program. Notice that the driver program name is always emit followed by the specified file stem. Therefore, the length of your file stem should not be more than four characters long on systems that only support an eight character ifie name.
 * emit.zfilestem>.def—An export file that contains export entries.
 * Makefile—A Makefile for creating a DLL for the new emitter.

The Output Template
The Emitter Framework provides a template facility that allows developers to specify the form of an output file in a readable and maintainable manner. Information about how the output file should look can be placed in a template file. It does not need to be specified in the emitter code. The template file is divided into sections. Each section specifies the desired output format for each syntactic unit of the interface definition.

Output templates are stored in files with an .efw (stands for Emitter Framework) extension. The newemit program generates a generic template file .efw. This file contains all the standard sections with sample text for each section. This file must be edited so it contains your desired output format.

Figure 9.1 is a partial listing of a generated template file. It contains the sections classS, attributePrologS, attributeS and attributeEpilogS. 
 * classS

Section: classS

className = "" class DLScopedName = "" classCScoped Name = '" classComment = "<-- classComment>"  classinclude = " "  classLineNumber = '" classMods = "" classMajorVersion = "" classMinorVersion = "" classReleaseOrder = "" classSourceFile = '" classSourceFileStem = ""


 * attribute PrologS

Section: attributePrologS


 * attributeS

Section: attributeS

attributeDeclarators = "" attributeBaseType = "" attributeComment = "<-- attributeComment>" attributeLineNumber = "" attributeMods = '"


 * attribute EpilogS

Section: attributeEpilogS  Figure 9.1 Sample template file

A new section is denoted by a line that starts with the colon. By convention, section names end in capital "S". The section classS represents the class section. The section attributePrologS represents the prolog for the attribute section. It is only emitted once regardless of how many attributes there are. The section attributeS represents the repeating portion. It is repeated once for every attribute. The section attributeEpilogS represents the epilog for the attribute section. It is only emitted once regardless of how many attributes there are.

A symbol is specified using angle brackets. It is used to represent a corresponding value. The output template above contains the symbols className, classlDLScopedName, classCScopedName, etc. When a section is emitted, these symbols are replaced with their actual values. For example, the symbol className will be replaced by the actual name of the class, and the symbol classIDLScopedName will be replaced by the scoped name of the class using "::" as delimiters.

The symbols classMods, ... and classReleaseOrder, ... represent a list substitution of symbols. The '.." indicates that list substitution is used and the symbol's value must consist of a sequence of items. The character "," is used as the separator character.

The symbols -- classGomment and -- attributeComment represent a comment substitution of symbols. When the "--" precedes a symbol name, it indicates that comment substitution is used and the symbol's value is emitted in comment form. You can control the style and format of the comment in your emitter program. For example, you can control whether comment uses the C++ style ("II"), or the C style (a/*" and "*/")

Using the Simple IDL of Figure 9.2 and the output template of Figure 9.1, the output of Figure 9.3 is produced. 
 * 1) include <somobj.idl>

interface Simple: SOMObject                 //Simple IDL Interface {  attribute short al;                       //attribute 1 attribute long a2;                       //attribute 2 #ifdef SOMIDL implementation {    releaseorder : _get_al, _set_al, _get_a2, —set a2; };  #endif };

</PRE> Figure 9.2 The simple IDL  Section: class

className = "Simple" classlDLscopedName "::Simple" classcscopedName = "Simple" classComment = '7/ Simple IDL Interface" Classinclude = " " classLineNumber = "0000003" classMods = "releaseorder = -get-al,-set-al,-get-a2,-set-a2, filestem = simple" classMajorversion = "0" classMinorvers ion = "0" classReleaseOrder = " -get _al, set al -get-a2, _set _a2" classSourceFile = "simpleicil" classSourceFileStem = "simple"

Section: attributeProlog5

Section: attributeS attribute Declarators = "al' attributeBaselype = "short" attributeComment "1/ attribute 1" attributeLineN umber = "0000005" attributeMods

Section: attributeS

attributeDeclarators = 'a2" attributeBaselype = "long" attributeComment = "II attribute 2" attributeLineNumber = "0000006" attributeMods =

Section: attribute EpilogS </PRE> Figure 9.3 The output template for simple

The Emitter Implementation
The newemit program generates an IDL definition and a default C implementation for your emitter. Your emitter is a subclass of SOMTEmitC and overrides the somtGenerateSections method. The somtGenerateSections method determines which sections of the output template are emitted, and in what order. Typically, you will customize the default implementation of somtGenerateSections. The somtGenerateSections method is called by the emitter driver program emit .c, when the emitter is invoked by the SOM compiler.

Figure 9.4 shows the default implementation of somtGenerateSections for the emitter class ReportEmitter.  SOM_Scope boolean SOMLINK somtGenerateSections(ReportEmitter somSelf) {   ReportEmitterData *somThis = ReportEmitterGetData(somSelf); SOMTClassEntryC cls = __get_somtTargetClass(somSelf); SOMTTemplateOutputC template = __get_somtTemplate(somSelf); ReportEmitterMethodDebug("ReportEmitter","somtGenerateSections");

/*    * Setup symbols that are common to the whole file */   _somtFileSymbols(somSelf);

_somtEmitProlog(somSelf);

if (cls != (SOMTClassEntryC) NULL) { _somtScanBases(somSelf,                      "somtEmitBaseIncludesProlog",                       "somtEmitBaseIncludes",                       "somtEmitBaseIncludesEpilog"); _somtEmitMetalnclude(somSelf);

_somtEmitClass(somSelf);

—somtScan Bases (somSelf,                         "somtEmitBaseProlog",                          "somtEmitBase",                          "somtEmitBaseEpilog"); _somtEmitMeta(somSelf); }   _somtScanConstants(somSelf, 'somtEmitConstantProlog",                       "somtEmitConstant", "somtEmitConstantEpilog");

_somtScanTypedefs(somSelf, "somtEmitTypedefProlog",                     "somtEmitTypedef", "somtEmitTypedefEpilog");

_somtScanStructs(somSelf, "somtEmitStructProlog",                   "somtEmitStruct", "somtEmitStructEpilog');

_somtScanUnions(somSelf, "somtEmitUnionProlog",                  "somtEmitUnion", "somtEmitUn ion Epilog');

_somtScanEnums(sQmSeIf, 11somtEmitEnumprojog                 'somtEmjtEnum 'somtEmitEnumEpjjog);

if (cls != (SOMTClassEntryC) NULL){ _somtScanAttributes(somSelf, "somtEmitAttributeprolog",                                 "somtEmitAttribute", "somtEmitAttributeEpilog");

_somtscan Methods (somSelf,                        "somtImplemented",                         "somtEmitMethodsprolog",                         "somtEmitMethod",                         "somtEmitMethodsEpilog",                         0);

_somtEmitRelease(somSelf);

_somtScanpassthru(somSelf, 1,                       "somtEmitPassthruProlog",                        "somtEmitPassthru",                        "somtEmitPassthruEpilog");

_somtScanpassthru(somSelf, 0,                       "somtEmitPassthruprolog",                        "somtEmitpassthru",                        "somtEmitPassthruEpilog);

_SomtScanData(somSef,                   "somtEmitDataprolog",                    "somtEmitData",                    "somtEmitDataEpilog"); }

if (__get somtTargetModule(somSelf) != (SOMTModuleEntryC) NULL){

_somtscanInterfaces (somSelf, "somtEmitInterf aceProlog",                             "somtEmitInterface", "somtEmitInterfaceEpilog");

_somtScanModules(somSelf, "somfEmitModuleProlog",                       "somtEmitModule", "somtEmitModuleEpilog"); }

_somtEmitEpilog(somSelf);

return (TRUE); } </PRE> Figure 9.4 The default implementation of somtGenerateSections

The somtEmit<Section> methods emit a particular section from an emitter's template. For example, the somtEmitClass method emits the class section.

The somtScan<Section> methods iterate through a repeating section and call the section-emitting methods whose names are specified in the somtScan<Section> method. For example, the somtScanConstants method iterates through the constants declarations and emits the constantS section for each constant. If a constantPrologS section is defined, it will be emitted before the first constant. If a constantEpilogS section is defined, it will be emitted after the last constant.

The default implementation of somtGenerateSections emits the template sections in the following order: You can change the order of these sections, or omit any section not relevant to your emitter.
 * 1) Prolog—text before any other sections
 * 2) Base Includes—base (parent) class include statements
 * 3) Meta Includes—metaclass include statements
 * 4) Class—class information
 * 5) Base—base (parent) classes information
 * 6) Meta—metaclass information
 * 7) Constant—user-defined constants
 * 8) Typedef—user-defined types
 * 9) Struct—user-defined structs
 * 10) Union—user-defined unions
 * 11) Enum—user-defined enumerations
 * 12) Attribute—attributes of the class
 * 13) Method—methods of the class
 * 14) Release—release order statement
 * 15) Passthru—passthru statements
 * 16) Data—internal instance variable of the class
 * 17) Interface—interfaces in a module
 * 18) Module—module information
 * 19) Epilog—text after all other sections

Building the Emitter
The newemit program generates a Makefile that you can use to build your emitter. When you invoke NMAKE, the emitter driver program and the emitter C implementation will be compiled and linked to create a DLL for your emitter. The name of the DLL is emit. This DLL should be placed in a directory that can be reached by your LIBPATH statement.

Invoking the Emitter
To invoke the emitter, run the SOM compiler using the -s option, specifying the name of the emitter. For example, the following command invokes the rep emitter on the test.idl file. sc -srep test.idl This will produce the file test, rep whose format is defined in the output template file rep.efw.

The entire development process for an emitter is shown in Figure 9.5.



Figure 9.5 The development process for creating an emitter

Emitter Framework Classes
The Emitter Framework consists of a number of classes as shown in Figure 9.6. The SOMTEmitC class manages the overall activity of an emitter. All emitters are derived from this class. The SOMTEmitC class provides the somtEmit<Section> and somtScan<Section> methods for emitting different sections of an output file.



Figure 9.6 Emitter Framework class hierarchy

The SOMTTemplateOutputC class controls the formatting part of the emitter process by providing a template facility. The template is defined in a file with an .efw extension and consists of section names and symbol names. When the emitter is run, the symbols are replaced by the appropriate values. The SOMTTemplateOutputC class predefines a set of section and symbol names. It also provides methods where an emitter can define new sections and symbols. We will see an example of this in our Report Emitter.

The SOMTEntryC class provides an abstraction for returning information about an IDL interface definition. When the SOM compiler parses an IDL file, it produces an object graph. Each node (entry) in the object graph is derived from some portion of the IDL definition. The SOMTEntryC class and its subclasses provide attributes and methods to access the corresponding entry in the object graph. For example, a SOMTClassEntryC object represents a complete class interface definition and provides methods for accessing the constants, types, structs, unions, enums, sequences, attributes, and methods defined within an interface statement.

The code fragment in Figure 9.7 shows how you can retrieve the class name and the list of attributes and their types. The somtTargetClass attribute returns the target class for the emitter. The cls object can then be used to retrieve the class information.  SOMTClassEntryC cls = _get_somtTargetClass(emitter); SOMlAttributeEntryC attrb; SOMTDataEntryC     myEntry; SOMTEntryC         attrType;

printf("Class Name: %s\n", get_somtEntryName(cls));

for ( attrb = _somtGetFirstAttribute(cls); attrb;     attrb = _somtGetNextAttribute(cls)) {    // Handles the case where there is a list of declarators for an attribute type. // For example: attribute short al, a2, a3; for ( myEntry = _somtGetFirstAttributeDeclarator(attrb); myEntry;          myEntry = _somtGetNextAttributeDeclarator(attrb)) {        printf ("Attribute Name: %s", _get_somtEntryName(myEntry)); }

attrType = _get_somtAttribType(attrb); printf( "Attribute Type %s\n", _get_somtEntryName(attrType)); } </PRE> Figure 9.7 Example to show how to use the Emitter Framework classes

A Report Emitter
In this section, we will build a report emitter. The report emitter produces a report that lists the following information: The format for the output template is shown in Figure 9.8.
 * Class name
 * Class comment
 * Class parent name
 * Attribute names and their types
 * Method names, their parameters, and return types
 * The total number of attributes and methods in an IDL

Note that the section summaryS is not a pre-defined section, and the symbols totalAttributes and totalMethods are not the standard symbols. We will show how to set the values of these symbols in our emitter. 
 * classS

Report on class <className>

=
======================================== ? Description:    <classComment> IDL source file: <classSourceFile> Parent Name:    <baseName>


 * attributePrologS

Attribute Definitions

=
========================================
 * attributeS

<attributeDeclarators, ...> <attributeBaseType>
 * attributeEpilogS


 * methodsPrologS

Method Definitions

=
========================================
 * methodsS

<methodType> <methodName>  <methodIDLParamList, ...>
 * methodsEpilogS


 * summaryS

Summary

=
======================================== Total number of attributes: <totalAttributes> Total number of methods:   <totalMethods> </PRE>

Figure 9.8 The output template for our report

Invoke the newemit program using the following command: newemit ReportEmifter rep This generates the file rep.efw. Edit this file so that its format is the same as the output template given in Figure 9.8.

The file rep.idl contains the IDL definition for ReportEmitter. We will add the following to this file: The changes that are made to the IDL are shown in Figure 9.9 in bold. 
 * somtEmitMethod—this method is redefined so that it can count the number of methods the emitter emits.
 * somtEmitAttribute—this method is redefined so that it can count the number of attributes the emitter emits.
 * somlnit—this method is redefined to initialize instance variables.
 * 1) ifndef ReportEmitter_idl
 * 2) define ReportEmitter_idl

interface ReportEmitter : SOMTEmitC { #ifdef __SOMIDL__ implementation {   //# Class Modifiers callstyle = oidl; //# Method Modifiers somtGenerateSections: override;
 * 1) include <scemit.idl>

somtEmitMethod: override; somtEmitAttribute: override; somInit: override;

short numOfAttributes; short numOfMethods; }; #endif /* __SOMIDL__ */ };

</PRE> Figure 9.9 The ReportEmitter IDL
 * 1) endif /* ReportEmitter_idl */

The file rep.c contains the C implementation for the ReportEmitter. We modify the default implementation of somtGenerateSections so that it only generates the sections that we need in our output template. We also add the implementation for somtEmitMethod, somtEmitAttribute, and somlnit. The modified rep.c appears in Figure 9.10.  /* *        File:    rep.c *       Author:    SOMObjects Emitter Framework. *    Contents:    Generic framework implementation for ReportEmitter. *        Date:    Mon Jan  3 18:24:38 1994. */


 * 1) define ReportEmitter_Class_Source
 * 2) include <rep.ih>
 * 3) include <stdio.h>

SOM_Scope boolean SOMLINK somtGenerateSections(ReportEmitter somSelf) {   ReportEmitterData *somThis = ReportEmitterGetData(somSelf); char buf[50];

SOMTClassEntryC cls = __get_somtTargetClass(somSelf); SOMTTemplateOutputC template = __get_somtTemplate(somSelf); ReportEmitterMethodDebug("ReportEmitter","somtGenerateSections");

/*    * Setup symbols that are common to the whole file */   _somtFileSymbols(somSelf);

if (cls != (SOMTClassEntryC) NULL) { _somtScanBases(somSelf,                      "somtEmitBaseProlog",                       "somtEmitBase",                       "somtEmitBaseEpilog"); _somtEmitClass(somSelf);

_somtScanAttributes(somSelf, "somtEmitAttributeProlog",                           "somtEmitAttribute", "somtEmitAttributeEpilog");

_somtScanMethods(somSelf,                        "somtImplemented",                         "somtEmitMethodsProlog",                         "somtEmitMethod",                         "somtEmitMethodsEpilog",                         0);

sprintf(buf, "%d", _numOfAttributes); _somtSetSymbolCopyBoth(template, "totalAttributes", buf);

sprintf(buf, "%d", _numOfMethods); _somtSetSymbolCopyBoth(template, "totalMethods", buf); _somtOutputSection(template, "summaryS"); }

return (TRUE); }

SOM_Scope void SOMLINK somtEmitMethod(ReportEmitter somSelf,                                        SOMTMethodEntryC entry) {   ReportEmitterData *somThis = ReportEmitterGetData(somSelf); SOMTTemplateOutputC template = __get_somtTemplate(somSelf);

ReportEmitterMethodDebug("ReportEmitter","somtEmitMethod"); ReportEmitter_parent_SOMTEmitC_somtEmitMethod(somSelf, entry);

_numOfMethods++; }

SOM_Scope void SOMLINK somtEmitAttribute(ReportEmitter somSelf,                                           SOMTAttributeEntryC att) {   ReportEmitterData *somThis = ReportEmitterGetData(somSelf); ReportEmitterMethodDebug("ReportEmitter","somtEmitAttribute");

ReportEmitter_parent_SOMTEmitC_somtEmitAttribute(somSelf,                                                     att); _numOfAttributes++; }

SOM_Scope void SOMLINK somInit(ReportEmitter somSelf) {   ReportEmitterData *somThis = ReportEmitterGetData(somSelf); ReportEmitterMethodDebug("ReportEmitter","somInit");

ReportEmitter_parent_SOMTEmitC_somInit(somSelf);

_numOfAttributes = 0; _numOfMethods = 0; } </PRE> Figure 9.10 The ReportEmitter class implementation

Notice how we use the somtSetSymbolCopyBoth method to set the values of the symbols totalAttributes and totalMethods. The somtOutputSection method is then used to output the summaryS section.

The emitter can then be built by invoking NNAKE. This creates the DLL emitrep.dll.

Invoking the Report Emitter
Make sure the file emitrep.dll is placed in a directory that is specified on your LIBPATH. Also make sure the output template rep.efw is placed in a directory that is specified by the environment variable SMINCLUDE. For example, the following setting will cause the SOM compiler to search the current directory and then the SOM include directory for the output template: set SMINCLUDE=.;%SOMBASE%\INCLUDE The test.idl in Figure 9.11 is provided as a test case. 
 * 1) include <somobj.idl>

interface Test: SOMObject { const unsigned short MAXSIZE = 50; union Foo switch (long) {    case 1: long x;     case 2: float y;     default: char z;  };

attribute Foo myfoo; attribute double mydouble; attribute any anyvalue; attribute sequence<long,MAXSIZE> longList;

void add(in string name); string query(in short index, inout octet aByte); long print(out boolean status);

#ifdef __SOMIDL__ implementation {   releaseorder : _get_myfoo, _set_myfoo, _get_mydouble, _set_mydouble, _get_anyvalue, _set_anyvalue, _get_longList, _set_longList, add, query, print; }; #endif }; </PRE> Figure 9.11 An IDL to illustrate the report emitter

To run the report emitter on test.idl, invoke the SOM compiler using the -s option. sc -srep test.idl This produces the file test.rep listed in Figure 9.12.  Report on class Test

=
======================================== IDL source file: test.idl Parent Name:    SOMObject

Attribute Definitions

=
========================================  myfoo  Foo mydouble double anyvalue any longList /* seq<        50> */ _IDL_SEQUENCE_long

Method Definitions

=
======================================== Foo  _get_myfoo void _set_myfoo  in Foo myfoo double _get_mydouble void _set_mydouble  in double mydouble any _get_anyvalue void _set_anyvalue  in any anyvalue /* seq<       50> */ _IDL_SEQUENCE_long  _get_longList void _set_longList  in /* seq<        50> */ _IDL_SEQUENCE_long longList void add  in string name string query  in short index, inout octet aByte long print  out boolean status

Summary

=
======================================== Total number of attributes: 4 Total number of methods:   11 </PRE> Figure 9.12: Output from the report emitter.