PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/15 TITLE : C++ Language News _Borland Language Support News_ ---------------------------------------------------------------- volume 1, number 8 June 14, 1993 ---------------------------------------------------------------- MIGRATING TO AN OBJECT-ORIENTED MINDSET I This issue begins a multi-part series on some of the more common issues needing to be addressed when programming in an object-oriented fashion. The basis of discussion will cen- ter around the implementing the simple stack data structure. From the onset, it needs to be clear that what follows is not the only way of looking at implementing a stack from a object-oriented (OOP) viewpoint. What follows is just a single interpretation of what is possible in C++. It also should be mentioned that this series of articles will at- tempt to be complete as far as it goes, but it should not be considered to be definitive in looking at every possible manner in which the various features of C++ can be inte- grated into a single application. To establish common ground, stacks will be defined here as a data structure in which items can be inserted and extract- ed in a last-in, first-out (LIFO) fashion. Discussion will begin at the creation of a stack accepting only integer values, however, this will be extended in the next article to a template class. Because stacks, by definition, do not have an upper bound as to size, all memory required to hold the items placed into the stack must be dynamic. Since an easy association is to allocate memory from the heap based on each item coming into the data structure, this memory quantum will be termed a node. Each of the item nodes will need to possess informa- tion the previous node going into the stack. Plus, from the initial description of the data structure, facilities will have to be made to actually add and delete item nodes from the stack. This forms the complete description of what is needed to implement a simplistic stack. A stack consists of an in- determinate number of nodes linked together, and the stack must allow for insertions and deletions. At this point, PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/15 TITLE : C++ Language News enough information is available to design the C++ class interface: class node { friend class stack; int value; node *next; node(const int &i) : value(i) { next = 0; } }; class stack { node *root; public: stack() { root = 0; } int operator<<(const int&); // push analog int operator>>(int&); // pop analog ~stack(); }; Here, a stack consists of a pointer to a node and possesses push and pop actions to insert and extract nodes from the structure itself. The details of actually dealing with the heapspace management are irrelevant at the interface level, however, these details will be considered next. Up to this point, discussion has centered about description and design. Now, the heritage of C comes into play when implementing the member functions. The push routine when implemented could take on the following form -- int stack::operator<<(const int &i) { node *p = new node(i); // allocate new node if (!p) return 1; if (root) { // add to non-empty stack p->next = root; root = p; } else root = p; // first insertion return 0; } PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/15 TITLE : C++ Language News It can be seen here that only the heapspace manipulation is needed to be considered here. The details of what is con- tained in each node is left to the node class. Likewise, some error checking is provided which can bubble information back to a calling program as to the success of insertion. The extraction function, which mirrors insertion will be given next: int stack::operator>>(int &i) { if (!root) // empty stack return 1; i = root->value; node *p = root->next; delete root; root = p; return 0; } Without going into the details of cleaning up a complete stack's heapspace allocations, here is a possible implemen- tation for the stack class's destructor -- stack::~stack() { while (root != 0) { // continue until empty int i; *this >> i; } } Placing the class definitions and member function implement- ations into a single file with a simplistic driving main() function would yield a working program. The following driver will do little, but it serves as a springboard to later discussion. main() { const int max = 4; stack stk; for (int i = 0; i < max; ++i) // fill stack stk << i; for (i = 0; i < max; ++i) { // empty stack int j; PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/15 TITLE : C++ Language News stk >> j; } return 0; } The next installment to this series will look into display- ing the contents of the stack via an iterator, along with changing the stack and node classes into template classes for reusability. HOT ISSUES Technical Support has occasionally received reports that de- bugging executables created with TLINK version 5.22b (found in the second patch for Borland C++ 3.1 -- BC31P2.ZIP) has not been possible. The central issue is that 5.22b does not maintain the proper case for function main() unless the /c switch is explicitly used with TLINK. The reason the debug- ger does not run through the startup code is due to its in- ability to find a stopping location. Using the /c TLINK switch should resolve this matter. DOS 1. How can I determine how much memory is being used by a program during execution? The size of a program cannot be determined statically be- cause the stack and near heap are dynamically sized at runtime. This indicates that the value found in the EXE header for program size will be incorrect. The following program display's the current (and correct) program image size in memory (in paragraphs) in the large memory model. Altering the size of the stack in the small memory models (tiny, small, & medium) will not be reflected in the value displayed; the value displayed will also change as heap- space is allocated and deallocated. The Memory Control Block (MCB) located one paragraph before the Program Seg- ment Prefix contains the correct image size at offset 0x03 from the beginning of the paragraph. PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/15 TITLE : C++ Language News #include #include extern unsigned _stklen = 4000U; // varying stack size // will change dis- // played image size main() { void far *size; size = MK_FP(_psp - 1, 0x03); cout << "size = " << *(unsigned int*) size << endl; return 0; } OS/2 1. The help views provided with Borland C++ for OS/2 do not automatically link to each other. For this reason, you may encounter hot links in a help window to which you cannot jump. The resolution for this is to run VIEW.EXE and have it load all the help files in one session. Doing this is quite simple: a. Open the Borland Help Icon View window. b. Make a copy of any help icon. c. Bring up the Settings dialog for the new icon. d. Change the Parameters entry to: bc.inf+guiref20.inf+ipfc20.inf+ipfcexmp.inf+ pmfun.inf+pmgpi.inf+pmhok.inf+pmmsg.inf+pmrel.inf+ pmwin.inf+pmwkp.inf e. Set the working directory to the Borland C++ BIN di- rectory. For example: C:\BCOS2\BIN f. Go to the General page and change the Title. When you urn this application, it willl be slower loading because it is loading all the help files. However, you can leave it open in the background while you work such that it will always be available. WINDOWS 1. How do I perform message processing for a printer abort function under OWL? PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/15 TITLE : C++ Language News The trick to servicing an abort dialog box under Object- Windows is using ProcessAppMsg(). This function call gives OWL a chance to hook the message. eg: int FAR PASCAL AbortProc(HDC Prn, int Code) { MSG Msg; while (!bUserAbort && PeekMessage(&Msg, NULL, NULL, NULL, PM_Remove) { if (!GetApplication()->ProcessAppMsg(Msg)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return (!bUserAbort); } } 2. How do I load a dialog from a DLL in ObjectWindows? In order for Windows to find a dialog resource, it must know its name, and the module which contains that resource. In standard Windows programming, this is done by passing an in- stance handle to the DLL which has the resource. ie. HANDLE hInst = LoadLibrary("RESOURCE.DLL"); DialogBox(hInst, "DIALOG_1", ...); In OWL, the instance handle of a DLL is encapsulated in the TModule object which was constructed in that DLL. What needs to be done is construct a TModule oject in the DLL which has the dialog (constructing a TModule with the hInst passed to the LibMain for that DLL) and then pass a pointer to this TModule object as the third parameter to TDialog. eg. ExecDialog(new TDialog(this, "DIALOG_1", PMyModule)); where pMyModule was constructed in the DLL which has the re- source. TURBO VISION 1. The following program shows how window focus can be changed. PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/15 TITLE : C++ Language News #define Uses_THistory #define Uses_MsgBox #define Uses_TProgram #define Uses_TApplication #define Uses_TKeys #define Uses_TRect #define Uses_TMenuBar #define Uses_TSubMenu #define Uses_TMenuItem #define Uses_TStatusLine #define Uses_TStatusItem #define Uses_TStatusDef #define Uses_TDeskTop #define Uses_TView #define Uses_TWindow #define Uses_TFrame #define Uses_TScroller #define Uses_TScrollBar #define Uses_TDialog #define Uses_TButton #define Uses_TSItem #define Uses_TCheckBoxes #define Uses_TRadioButtons #define Uses_TLabel #define Uses_TInputLine #define Uses_TMenu #include #include #include #include const int cmAbout = 100; const int cmDialog1 = 101; // // define, instantiate and initialize the dialog trans- // fer structure // struct dialogstruct { ushort chk; ushort rad; char first[30]; PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/15 TITLE : C++ Language News char last[30]; } s = { 0, 0, "", "" }; // // main application class // class TMyApp : public TApplication { protected: void Dialog1(void); public: TMyApp(); static TMenuBar* initMenuBar(TRect); static TStatusLine* initStatusLine(TRect); void handleEvent(TEvent&); }; // // subclass TDialog class to add new functionality // class myDialog : public TDialog { // // Keep a pointer to the radiobutton in the dialog. // This pointer will be used later in the handleEvent // of the class. // TRadioButtons *radioPtr; public: myDialog(const TRect&, const char*); void handleEvent(TEvent&); }; // // class constructor for new dialog class "myDialog" // myDialog::myDialog(const TRect &bounds, const char*aTitle) : TDialog( bounds, aTitle), TWindowInit(initFrame) { TView *b; // // make and insert checkBoxes PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/15 TITLE : C++ Language News // b = new TCheckBoxes(TRect( 3, 3, 41, 7), new TSItem("Assembly", new TSItem("Basic", new TSItem("Debugger", new TSItem("Profiler", new TSItem("Engine", new TSItem("Windows", new TSItem("Letters", new TSItem("OWL", new TSItem("TV", new TSItem("DOS", new TSItem("OS/2", new TSItem("C++", 0))))))))))))); insert(b); insert(new TLabel(TRect(2, 2, 10, 3), "~P~roduct", b)); // // make and insert the radioButtons // b = new TRadioButtons(TRect( 3, 10, 41, 13), new TSItem("Do Nothing", new TSItem("Check All and enter Name", new TSItem("Clear All fields", 0)))); insert(b); insert(new TLabel(TRect(2, 9, 13, 10), "~M~ain Duty", b)); // // set the class global pointer to the radiobuttons // pointer // radioPtr = (TRadioButtons*) b; // // make and insert the name inputline // b = new TInputLine(TRect(3, 15, 41, 16), 30); insert(b); insert(new TLabel(TRect(2, 14, 34, 15), "~F~irst Name", b)); insert(new THistory(TRect(39, 15, 42, 16), (TInputLine*) b, 101)); // // make and insert the last name inputline PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/15 TITLE : C++ Language News // b = new TInputLine( TRect( 3, 18, 41, 19 ), 30); insert(b); insert(new TLabel(TRect(2, 17, 34, 18), "~L~ast Name", b)); insert(new THistory(TRect(39, 18, 42, 19), (TInputLine*) b, 102)); // // make and insert buttons // insert(new TButton(TRect(5, 20, 13, 22), "~O~k", cmOK, bfDefault)); insert(new TButton(TRect(28, 20, 38, 22), "~C~ancel", cmCancel, bfNormal)); // // set the initial data into the dialog box // setData(&s); } // // If the event is a broadcast of type cmReleasedFocus // and it was sent by the radioButton then getData(), // take action base on the current value of the // radioButtons // void myDialog::handleEvent(TEvent& event) { if (event.what == evBroadcast && event.message.command == cmReleasedFocus) { if (event.message.infoPtr == radioPtr) { getData(&s); if (s.rad == 1) { strcpy(s.first, "Frank" ); strcpy(s.last, "Borland" ); s.chk = 4095; setData(&s); } else if (s.rad == 2) { strcpy(s.first, ""); strcpy(s.last, ""); s.chk = 0; setData(&s); PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/15 TITLE : C++ Language News } } } // // Call TDialog's handleEvent after our processing // otherwise it will handle the cmReleasedFocus events // TDialog::handleEvent(event); } // // TMyApp member function which creates & executes // a "myDialog" // void TMyApp::Dialog1() { myDialog *pd = new myDialog(TRect(18, 0, 62, 23), "Operator Input"); if (pd) { deskTop->execView(pd); destroy(pd); } } TMyApp::TMyApp() : TProgInit(&initStatusLine, &initMenuBar, &initDeskTop) { } // // TMyApp handleEvent processes the menuBar and status- // Line commands // void TMyApp::handleEvent(TEvent& event) { TApplication::handleEvent(event); if (event.what == evCommand) { switch(event.message.command) { case cmDialog1: Dialog1(); break; default: break; } PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 12/15 TITLE : C++ Language News clearEvent(event); } } TStatusLine* TMyApp::initStatusLine(TRect r) { r.a.y = r.b.y - 1; return new TStatusLine(r, *new TStatusDef(0, 0xFFFF) + *new TStatusItem("~Alt-X~ Exit", kbAltX, cmQuit) + *new TStatusItem("~Alt-A~ About", kbAltA, cmDialog1)); } TMenuBar* TMyApp::initMenuBar(TRect r) { r.b.y = r.a.y + 1; TMenuItem *one = new TMenuItem("~E~xit", cmQuit, kbAltX, hcNoContext, 0, 0); TMenuItem *two = new TMenuItem("~A~bout", cmAbout, kbAltA, hcNoContext,0, one); TMenuItem *three = new TMenuItem("~D~ialog", cmDialog1, kbAltD, hcNoContext, 0, two); return new TMenuBar(r, new TMenu(*three)); } int main() { TMyApp myApp; myApp.run(); return 0; } PARADOX ENGINE 1. I am getting a "Error 15: Multiple Paradox net files found" message. Why? PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 13/15 TITLE : C++ Language News One PARADOX.NET or PDOXUSRS.NET file controls access to any given table. This error occurs when two applications using different net files tyr to access the same table. A common cause of this error results from the fact that the Paradox Engine treats different logical drives (drive mappings) as unique drives. For example, say that a physical drive has been mapped to logical drives P: and Q:. The Engine treats P:\PARADOX.NET as a different net file than Q:\PARADOX.NET even though they reference the same physical location. Set both applications to use the same logical drive drive. This error is also caused by using the same directory for controlling version 3.5 and version 4.0 locking mechanisms. This would result in both a PARADOX.NET and PDOXUSRS.NET file existing within the same directory. Delete the *.NET files, and change the net directory of one of the appli- cations. PASCAL 1. What is done differently in Borland Pascal's DOS extender? Although most Pascal programs written under TP60 will recom- pile unchanged as protected mode apps, developers should be aware of the way memory is addressed in protected mode. Real mode DOS addresses typically include a segment and offset which reference an actual location in memory. But under Intel's protected mode memory scheme, the segment portion of an address is replaced with something called a selector. Selectors are not real memory addresses but rather indexes into a descriptor table. This table is maintained by the operating system and cannot normally be accessed directly by a programmer. This means that programs which directly refer- ence real mode memory addresses will have to be adjusted be- fore they will run correctly in protected mode. Fortunately, Intel has provided programmers with a DOS Protected Mode In- terface (DPMI) specification, which forms a gateway between protected mode and real mode. Borland's new protected mode DOS extender is DPMI compliant, so this interface is avail- able to experienced programmers who want to address real-mode memory directly. As a result, Borland Pascal protected mode apps have almost unlimited access to real mode. Your pro- PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 14/15 TITLE : C++ Language News tected mode applications will also run inside a Windows DOS box. This is possible because Windows itself runs in pro- tected mode and is somewhat DPMI compliant. QUIZ The subtlety in last week's quiz question is the dilemma between who is initializing who. In the initialization list for the derived class 'bar', the base class constructor is receiving the output from member function f() defined in the base class 'foo'. The assert() statement compares the member variable 'v' against the value 1, but 'v' has at this point no value associated to it. This indicates that whatever val- ue resides at the location 'v' from a previous application is what will be compared to 1. Though the possibility is present that this will be 1 (which will allow the member function to terminate normally), probability indicates that some value other than 1 is more likely. This is why the assert() error is being generated. This week's quiz centers about designing a class 'foo' which adheres to the following two conditions: 1. An instance of class 'foo' can be created by anyone and anywhere within a program. 2. Any attempt to derive a class from class 'foo' will re- sult in a compile time error when the derived class is instantiated. NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1381 Explanation about deriving a new class from class 'Object' when using the ob- ject-based class libraries. 1338 Overview of Borland C++ for OS/2 which is currently available. 1171 Borland Languages Problem Report Form which can be used to report problems PRODUCT : Borland C++ NUMBER : 8507 VERSION : All OS : All DATE : October 25, 1993 PAGE : 15/15 TITLE : C++ Language News and anomalies experienced with one of the Borland Language products. 740 New product line overview. 741 Common questions & answers on new pro- duct announcement. These documents can be found on either LIB-2 of BCPPDOS or BCPPWIN on CompuServe, Borland's DLBBS at (408)431-5096, TechFAX at (800)822-4269, or OAS at (408)431-5250. DISCLAIMER: You have the right to use this technical information subject to the terms of the No-Nonsense License Statement that you received with the Borland product to which this information pertains.