With Class C++ Code Generation for Borland C++ and Visual C++ by Richard Felsinger, RCF Associates, 960 Scottland Dr, Mt Pleasant, SC 29464 telephone 803-881-3648 - CompuServe 71162,755 3/7/94 The purpose of article is to present the "how to" steps to generate C++ code using the With Class CASE (computer aided software engineering) tool. With Class is available in a shareware version from MicroGold Software 696 Birch Hill Drive, Bridgewater, NJ, 08807 telephone 908-722-6438 - CompuServe 71543,1172. On CompuServe, you may find With Class in the Borland Forum - Borland C++ for Windows - Non Tech Cust Service with the file name wclass.zip. The current With Class Version 1.6 generates either ANSI C++ compilable code, Borland C++ Version 3.1 with OWL 1.0 compilable code, or Visual C++ with MFC 2.0 compilable code. To generate C++ for Borland C++ 4.0 with OWL 2.0 or Microsoft Visual C++ for QuickWin, you must first generate ANSI C++ and then manually update the code for the appropriate libraries. Guidelines will be presented. Using With Class, you first create a class diagram showing the classes, attributes, operations, and relationships. You fill in specification forms (dialog boxes) to add additional information about each class, attribute, operation, and relationship. With Class generates the following ANSI C++ compilable code from a class diagram: - #ifdefs, - #includes, - default constructor, - constructor with arguments, - copy constructor, - default destructor, - =operator (assignment operator), - ==operator (equality operator), - get and set functions for data members, - pointer data member for 1 to 1 and 1 to many associations using C array, - data member for 1 to 1 and 1 to many aggregations using C array, - inheritance relationships. With Class generates the following C++ for the C++ iostreams: - operator<< insertion operator for cout statements, - operator>> extraction operator for cin statements. These operators may be used in Borland C++ and Visual C++ under the QuickWin project option. With Class generates streamable classes for persistence only for Borland C++ using OWL 1.0. To create streamable classes for Borland C++ 4.0 with OWL 2.0, manually update your code. Likewise, to create serial classes for Visual C++ with MFC 2.0, manually update your code. If specified by the user, With Class generates the following: #include statements for external files (library files), initial data member values in constructors, virtual functions, pure virtual functions, static functions, and const functions. A user must manually insert the following keywords: static for data members and friend for functions. If you use the "Insert the prefix T" option you must manually update your constructor and destructor names with the prefix. Generating C++ for Borland C++ 3.1 with OWL 1.0 The following are the steps to generate C++ for Borland C++ 3.1 with OWL 1.0. >> Run With Class from Windows >> Select "File - Open", e.g., c:\car\car.omt >> Select "Generate - Options" and select C++ code generation options >> Select "Generate - Generate Code" >> Select "File - Edit File" and select a ".h" or ".cpp" file for review Once the code is generated, then the user may go to a C++ compiler to compile, link, and execute the code. To use the Borland C++ 3.1 environment, the following are sample environment settings for Borland C++ Options: Application: Large Memory Model Linker Link Libraries: Static for all libraries Include Directories: c:\bc\include; c:\bc\owl\include;c:\bc\classlib\include; Library Directories: c:\bc\lib; c:\bc\owl\lib; c:\bc\classlib\lib; Compiler - Code Generation - Defines WIN31 The steps to Compile the C++ Source Files in Borland C++ are: >> Run Borland C++ (BCW) from Windows >> Select "Project - Open Project" >> Enter a project file name, e.g., carproj.prj >> Select "Project - Add Item" >> In the Directories Box, change the directory where the C++ files are located, e.g., Car Directory >> Enter *.cpp in the File Name Box >> Select the .cpp files, e.g., main.cpp and car.cpp and click on the "Add Button" to add each file to the project >> Select "Done" >> Select "Options - Directories" to update the Include Directories List >> In the Include Directories Box, add the directory where the C++ files are located, e.g., c:\car >> Select "OK" >> Select "Compile - Compile" to compile the C++ source code >> Select "Run - Run" to compile, link, and execute To execute the program the user must create a main function. The steps to create a main function and execute the program in Borland C++ are listed below. To execute the C++ source code with messages, you must update the main module with an object declaration and messages to the object. >> Select "File - Open" >> Select the main function, e.g., c:\car\main.cpp >> Enter the C++ statements for the main, e.g., statements shown below >> Select "File - Save" >> Select "Compile - Compile" to compile the main function >> Select "Run - Run" to compile, link, and execute the program #include "car.h" //Added code to use generated C++ main () { Car car11; //object declaration car11.setGasQty (10.0); //function call car11.start (); //function call int aGasQty = car11.getGasQty (); //function call return (0); } Generating Streamable Classes for Borland C++ 3.1 with OWL 1.0 A persistent object retains its values when the program is not running, e.g., the system is not active. The three primary ways to create persistent objects are with flat file storage, relational data base management system storage, or object oriented data base storage. With Class generates C++ streamable classes (Borland C++) for persistent objects with the following steps: >> Select "Make All Classes Streamable", "Get Methods", and Set Methods" from the Options Menu. >> Select "Generate Code" from the Generate Menu. >> Update the generated code as follows: Create a stream object, e.g. inputStream or outputStream. Open the stream object, e.g. inputStream.open ("CAR.STM"). Read from or write to the stream object, e.g. inputStream >> car1 or outputStream << car1. Close the stream object, e.g. inputStream.close (). As an example, a class diagram was created with a single class Car with the data member "float gasQuantity". With Class automatically generated the get and set functions: "getGasQuantity" and "setGasQuantity". The following main function shows how to create, open, read from, write to, and close a C++ a file stream. In this example, the car1 object was written to and read from a C++ file stream for persistent storage. #include "car.h" //Added code to use generated C++ #include int main () //Implements MainUser { Car car1; car1.setGasQuantity (20.0); ofpstream outputStream; //Creates file stream outputStream.open ("CAR.STM"); //Opens file stream if (outputStream.bad () ) cout << "Could not open file/n"; outputStream << car1; //Writes to file stream outputStream.close (); //Closes file stream Car car2; ifpstream inputStream; //Creates file stream inputStream.open ("CAR.STM"); //Opens file stream if (inputStream.bad()) cout << "Could not open file/n"; inputStream >> car2; //Reads from file stream cout << car1.getGasQuantity (); cout << car2.getGasQuantity (); inputStream.close(); //Closes file stream return 0; } Generating ANSI C++ for Borland C++ 4.0 with OWL 2.0 To generate C++ for Borland C++ 4.0, you must first generate ANSI C++ then manually update the code for OWL 2.0. In the With Class Options dialog, the following are guidelines: - check "#ifdef", - check "Get and Set Methods", - check "ostream << to display classes", - check "C Array", - do not check "Make All Classes Streamable" - OWL 1.0 only, - do not check "C++ Collection Templates" - OWL 1.0 only. The steps to generate C++ for Borland C++ 4.0 with OWL 2.0 are similar to the steps previously stated for Borland C++ 3.1. After code has been generated the code can be manually updated for streamable classes and OWL 2.0 collection classes. OWL 2.0 collection classes may be used to implement the "1 to many" relations. The following OWL 2.0 classes may be used: TListImp, TArray, TStack, and TBag. The following short program shows these classes with their appropriate #include file. #include //for cout #include //for string class #include //for TListImp class #include //for TArray class #include //for TBag class #include int main () { cout << "hello\n"; string name1 ("dick"); string name2 ("geni"); cout << "Enter your name\n"; cin >> name1; cout << name1; TListImp aList; //List class aList.Add (name1); aList.Add (name2); aList.Detach (name2); TSListImp aSortedList; //Sorted List class aSortedList.Add (name1); aSortedList.Add (name2); aSortedList.Detach (name2); TArray anArray (10); //Array class anArray.Add (name1); anArray.Add (name2); anArray.Detach (name2); TSArray aSortedArray (10); //Sorted Array class aSortedArray.Add (name1); aSortedArray.Add (name2); aSortedArray.Detach (name2); TStack aStack; //Stack class aStack.Push (name1); aStack.Push (name2); aStack.Pop (); TBag aBag; //Bag class aBag.Add (name1); aBag.Add (name2); aBag.Detach (name2); return 0; } The following header file shows the use of the TListImp class to implement a 1 to many aggregation between the car class and the motor class. In this example the car has many motors. #ifndef __CAR_H #define __CAR_H #ifndef __MOTOR_H #include "Motor.h" #endif #ifndef __LISTIMP.H //added #include //added #endif //added class Car { TListImp motors; //updated - implements 1 int speed; //to many relationship public: void start(); Car() { speed = 0; } Car(int aSpeed) { speed = aSpeed; } ~Car(){} Car(const Car& aCar) { // Copy Constructor speed = aCar.speed; } Car operator= (const Car& aCar) { //Assignment Operator if (this == &aCar) return *this; speed = aCar.speed; return *this; } int operator==(const Car& aCar) { //Equality Operator return ( (speed == aCar.speed) ); } int getSpeed() const {return speed; } void setSpeed(int aSpeed) {speed = aSpeed; } }; #endif Generating ANSI C++ for Visual C++ with QuickWin (No MFC) The easiest way to use With Class and Visual C++ is to create a QuickWin project. This will permit the use of the C++ iosteam objects, e.g. cin and cout. You must generate ANSI C++. In the With Class dialog box, select the following: - select "#ifdef", - select "Get and Set Methods", - select "Copy Constructor and Assignment Operator", - select "ostream << to Display Classes", - select "C Array" for 1 to many relationships. - do not select "C++ Collection Templates" - uses a class library, - do not select "Make All Classes Streamable" - OWL 1.0 only For 1 to many relationships, select "C Array". Then, if desired, update the code manually for MFC 2.0 collection classes. The following are the steps to generate ANSI code using With Class. >> Run With Class from Windows >> Select "File - Open", e.g., c:\car\car.omt >> Select "Generate - Options" and select C++ code generation options. Do not select "Make All Classes Streamable" or "C++ Collection Templates" >> Select "C Array" for 1 to many relationships >> Select "Generate - Generate Code" >> Select "File - Edit File" and select a ".h" or ".cpp" file for review Once the code is generated, then the user may go to Visual C++ to compile, link, and execute the code. To use the Visual C++ environment, select defaults and then update the Linker Library options with COMMDLG and SHELL. Select "Linker" and then "Miscellaneous" and enter /NOE. The steps to compile, link, and execute the C++ in Visual C++ are: >> Run Visual C++ from Windows >> Select "Project - New" to create an new project" >> Enter a project file name, e.g., carproj and select "QuickWin Application (.EXE) and select "Use Microsoft Foundation Classes" >> Enter a main file, e.g. main.cpp >> Select "Add" or "Add All" to add your .cpp files to the project >> Select "Project - Options" >> Select "Linker" and then "Windows Libraries". Select COMMDLG and SHELL >> Select "Linker" and then "Miscellaneous" and enter /NOE >> Select "File - Open" to open a .cpp file e.g. main.cpp >> Select "Project - Compile File" to compile the C++ source code >> Select "Project - Build" to build the project >> Select "Project - Execute" to execute the project The following ANSI C++ was generated by With Class for a car class. In this example a car has many motors. ////////////////////////Header File/////////////////////////////// #ifndef __CAR_H #define __CAR_H #ifndef __MOTOR_H #include "Motor.h" #endif #ifndef __IOSTREAM_H #include #endif class Car { Motor motors[100]; //Array to implement 1 to many relationship int gasQty; public: void start(); Car() { gasQty = 0; } Car(int aGasQty) { gasQty = aGasQty; } ~Car(){} Car(const Car& aCar) { // Copy Constructor gasQty = aCar.gasQty; } Car operator= (const Car& aCar) { //Assignment Operator if (this == &aCar) return *this; gasQty = aCar.gasQty; return *this; } int operator==(const Car& aCar) { //Equality Operator return ( (gasQty == aCar.gasQty) ); } int getGasQty() const {return gasQty; } void setGasQty(int aGasQty) {gasQty = aGasQty; } friend ostream& operator<< (ostream& os, Car& aCar); friend istream& operator>> (istream& is, Car& aCar); }; #endif ////////////////////Source Code File///////////////////////// #include "Car.h" ostream& operator<< (ostream& os, Car& aCar) { os << "Class Car" << "\n\n"; os << " gasQty: " << aCar.gasQty << '\n'; return os; } istream& operator>> (istream& is, Car& aCar) { cout << "Class Car" << "\n\n"; cout << "\nEnter gasQty: " << "\n\n"; is >> aCar.gasQty; return is; } void Car::start() { }; Generating C++ for Visual C++ with MFC 2.0 with AppWizard With Class generates compilable C++ for MFC 2.0. For 1 to many relationships, With Class generates lists and arrays with the MFC classes CObList and CObArray. The following is a code listings showing MFC 2.0 collection classes and their #include file. #include //For cout #include //For CString #include //For collection classes class Person : public CObject //Class to create collection elements { }; int main () { cout << "The person's name is "; CString name ("Dick"); cout << name; //CString object CStringList aStringList; //String List aStringList.AddHead (name); aStringList.RemoveTail (); Person person1; CObList personList; //Add to Head and Remove from Tail personList.AddHead (&person1); personList.RemoveTail (); CObList personStack; //Add and Remove from Head personStack.AddHead (&person1); personStack.RemoveHead (); CObList personQueue; //Add to Tail and Remove from Head personQueue.AddTail (&person1); personQueue.RemoveHead (); CObArray personArray; //Add and Remove an indexed element personArray.InsertAt (0, &person1); personArray.RemoveAt (0); return 1; } To generate C++ using With Class, the following are guidelines to select options in the C++ code generation dialog box: - select "#ifdef", - select "Get and Set Methods", - select "C++ Collection Template" for Array or List - select "Visual C++" - ensure #include "stdafx.h" is the first include file in each .cpp file. - do not select "Copy Constructor and Assignment Operator" to avoid conflicts with CObject class, - do not select "Make All Classes Streamable" - this is for OWL 1.0 only - do not select "ostream << to Display Classes - this uses iosteam.h The following are the steps in Visual C++ to compile, link, and execute a program. >> Select "Project - AppWizard" to create a new project >> Select "Project - Edit" to add .cpp files to the project >> Select "File - Open" to open the doc file, e.g. "xxxdoc.h" >> Update the "xxxdoc.h" file to include a new .h file >> Select "Project - Compile File" to compile the document file, e.g. "xxxdoc.cpp" >> Select "Project - Build" >> Select "Project - Execute" After you use the AppWizard, update the "xxxdoc.h" file to include at least one of your new classes and declare at least one object. The following code fragment was added to the xxxdoc.h class to include the Car class: #ifndef __CAR_H //added #include "Car.h" //added #endif //added Car car1; //added as a data member The following header and source code files show the use of the CObList class to implement a 1 to many aggregation between the car class and the motor class. In this example the car has many motors. ///////////////////////////Header File////////////////////////// #ifndef __CAR_H #define __CAR_H #ifndef __MOTOR_H #include "Motor.h" #endif #ifndef __AFXCOLL_H #include #endif class Car { CObList motors; //implements 1 to many - Ensure class Motor int gasQty; //is a derived class of CObject public: void start(); Car() { gasQty = 0; } Car(int aGasQty) { gasQty = aGasQty; } ~Car(){} int getGasQty() const {return gasQty; } void setGasQty(int aGasQty) {gasQty = aGasQty; } }; #endif //////////////////////Source Code File/////////////////// #include "stdafx.h" //Important - Must be first #include "Car.h" void Car::start() { }; The following header file is the Motor class. Note that Motor is a derived class of CObject. This is required so that motor objects can be stored in the object of the CObjList to implement the 1 to many relationship. //////////////////////////////////Header File///////////////////// #ifndef __MOTOR_H #define __MOTOR_H class Motor : public CObject //Derived class of CObject { int rpm; public: void operate(); Motor() { rpm = 0; } Motor(int aRpm) { rpm = aRpm; } ~Motor(){} int getRpm() const {return rpm; } void setRpm(int aRpm) {rpm = aRpm; } }; #endif Summary The purpose of article was to present the "how to" steps to generate C++ code using the With Class CASE (computer aided software engineering) tool. It presented guidelines for updating generated ANSI C++ to use Borland OWL 2.0 and Microsoft MFC 2.0 class libraries for collections. Comments and suggestions on this article are solicited. Richard Felsinger CompuServe 71162,755 or telephone 803-881-3648. About RCF Associates - RCF Associates is a small software consulting company in Mt Pleasant, SC that specializes in training and developing object-oriented systems using object-oriented methodologies (Rumbaugh OMT, Coad-Yourdon, Booch, Fusion), CASE tools, object-oriented languages (C++, Ada, Eiffel), and Microsoft Windows applications (Access, Visio, Office).