PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/16 TITLE : C++ Language News -- Volume 1 Number 11 _Borland Language Support News_ ---------------------------------------------------------------- volume 1, number 11 July 19, 1993 ---------------------------------------------------------------- MIGRATING TO AN OBJECT-ORIENTED MINDSET IV In this final installment in this series, we look closely at both the strengths and weaknesses in the design and imple- mentation of the template stack class (and iterator) discuss- ed in the last three issues of this newsletter. In designing the template stack class, the peripheral node class had to be placed in the global name space. This may be considered to be a drawback. Given that classes can be nested, the node class could be moved into the scope of the stack class proper, however, moving will not be dealt with in this article. This might be an interesting exercise for the reader to implement. Likewise, in terms of design, the push and pop routines have been implemented with the << and >> operators overloaded. This gives the appearance of the template stack class to act as a stream. As it stands now, this is not thoroughly mimiced when the following is con- sidered: cout << "2 + 2 = " << (2 + 2) << endl; The initial premise for returning an integer value to the calling function after a push or pop from the stack was to provide a means of error checking. Assuming that this is too ambitious and that the above behaviour is desired, both push and pop routines can be altered to the following: template stack& stack::operator<<(const T &t) { // push analog node *p = new node(t); assert(p != 0); if (root) { p->next = root; root = p; } else PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/16 TITLE : C++ Language News -- Volume 1 Number 11 root = p; ++n; return *this; } template stack& stack::operator>>(T &t) { // pop analog assert(root != 0); t = root->value; node *p = root->next; delete root; root = p; --n; return *this; } In addition, back in the second installment of this series (volume 1 number 9), the stack class's destructor was im- plemented as: template stack::~stack() { while (root) { T t; *this >> t; } } This is inefficient due to the copying of each node's value into t and then destroying both the value and t. This can be streamlined to the following to get rid of the intermedi- ate T instance: template stack::~stack() { while (root) { node *p = root->next; delete root; root = p; } } PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/16 TITLE : C++ Language News -- Volume 1 Number 11 The intent of this series of articles has been to look at a simple data structure and implement it as a class template in an object-oriented fashion. Minimal testing of it has been presented. Template classes provide great flexibility, but the programmer in using them will need to ensure that all contingencies have been met. In particular, what is required by instantiating a stack on type foo? Looking at the code below, it can be seen from the node template class that a copy constructor will be needed, as well as overload- ing of the assignment operator within both the push and pop member functions. Because of the generic nature of contain- er classes, these types of issues will frequently be en- countered. Given below is a simple example of use of the corrected stack template class for both integers and the user-defined type 'foo'. #include #include class foo { // esoteric example int v; public: foo() { } foo(const int i) : v(i) { } foo(const foo &f) : v(f.v) { } foo& operator=(const foo &f) { v = f.v; return *this; } ~foo() { } friend ostream& operator<<(ostream &out, const foo &f) { return out << "foo(" << f.v << ")"; } }; template struct node { friend class stack; friend class iter; T value; node *next; node(const T &t) : value(t) { next = 0; } PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/16 TITLE : C++ Language News -- Volume 1 Number 11 ~node(); }; template class stack { friend class iter; node *root; unsigned n; public: stack() { n = 0; root = 0; } stack& operator<<(const T&); stack& operator>>(T&); unsigned size() { return n; } unsigned size() const { return n; } ~stack(); }; template class iter { const stack &r; node *p; public: iter(const stack &stk) : r(stk) { reset(); } void reset() { p = r.root; } unsigned size() { return r.size(); } T& operator()(); }; template stack& stack::operator<<(const T &t) { // push analog node *p = new node(t); assert(p != 0); // check heap if (root) { // non-empty p->next = root; root = p; } else // empty list root = p; ++n; return *this; } PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/16 TITLE : C++ Language News -- Volume 1 Number 11 template stack& stack::operator>>(T &t) { // pop analog assert(root != 0); // filled? t = root->value; node *p = root->next; delete root; root = p; --n; return *this; } template stack::~stack() { while (root) { node *p = root->next; delete root; root = p; } } template T& iter::operator()() { node *q = p; // save current if (p->next) // increment p = p->next; else reset(); return q->value; } template ostream& operator<<(ostream_withassign &out, const stack &stk) { const int w = 6; out.width(w); out << "["; iter it(stk); for (int i = 0; i < it.size(); ++i) { out.width(w); out << it(); PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/16 TITLE : C++ Language News -- Volume 1 Number 11 } out.width(w); return out << "]"; } main() { cout << "*****" << endl; const int n = 2; stack stk0; // foo stack for (int i = 1; i <= n; ++i) stk0 << foo(i) << foo(i * 10); cout << stk0 << endl; foo f, g; stk0 >> f >> g; cout << "popped values = " << f << ", " << g << endl; cout << stk0 << endl; stack stk1; // integer stack for (i = 1; i <= n; ++i) stk1 << i << i * 10; cout << stk1 << endl; int j; stk1 >> i >> j; cout << "popped values = " << i << ", " << j << endl; cout << stk1 << endl; return 0; } HOT ISSUES Users attempting to install Borland C++ for OS/2 on the D: drive of dual-boot FAT system may experience an error mes- sage stating that the file or directory cannot be found. This can be corrected by temporarily creating an empty OS2 directory off of the root directory on D: with empty DLL, APPS, and HELP directories underneath OS2. This will allow installation to proceed normally. DOS PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/16 TITLE : C++ Language News -- Volume 1 Number 11 1. How do I perform a warm and cold boot in C++? A cold reboot will take place if application execution is branched to FFFFh:0000h. A warm reboot which is the same as a cold boot without checking memory will occur if the value 1234h is poked into 0000h:0472h. The following code implements this scenario: #include #include #include #include main(int argc, char *argv[]) { void (far *fp)(); const int n = 2; // #required arguments if (argc != n) { cerr << "insufficient arguments" << endl; exit(1); } if (strcmp(strupr(argv[1]), "WARM") == 0) { cout << "warm booting..." << endl; unsigned far *p = (unsigned far*) MK_FP(0x000, 0x0472); *p = 0x1234; // poke value } else if (strcmp(argv[1], "COLD") == 0) cout << "cold booting..." << endl; else { cerr << "unknown argument: " << argv[1] << endl; exit(1); } fp = (void(far*)())MK_FP(0xffff, 0x0000); (*fp)(); // branch execution return 0; } OS/2 1. How can I use Borland C++ for OS/2 with IBM's Workframe/2? IBM allows third party vendors to design and implement their PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/16 TITLE : C++ Language News -- Volume 1 Number 11 command line programming tools under the IBM Workframe/2. Borland C++ for OS/2 is designed to allow compilation and linking under the IBM Workframe/2 programming environment. NOTE: Make sure the HELP environment variable has the Bor- land C++ \BIN directory listed within. Bring the CONFIG.SYS file up in the Borland IDE and search for the HELP environ- ment variable. For Workframe/2 to properly use Borland C++ the \BIN directory should be listed in the HELP variable. WORKFRAME READY EXAMPLE: SET HELP=C:\OS2\HELP;C:\BCOS2\BIN; WORKFRAME NOT READY EXAMPLE: SET HELP=C:\OS2\HELP; To properly install Borland C++ under the IBM Workframe/2: a. Start IBM Workframe from workplace shell. b. Choose under the TOOLS menu the Language Profile Management item. c. In the Filename input line enter a filename that will be the language profile file. The name BCOS2 is perfectly acceptable and causes Workframe to create a file named BCOS2.PRF that contains the language profile information. d. In the Language Name enter a unique language name that will identify the Borland C++ language profile inside the IBM Workframe. The name "Borland C++" is an example. e. The Language Eyecatcher must be a unique name not listed in the Eyecatcher list box located in the bottom right of the dialog box. The Eyecatcher is a unique 8 character significant identifier that al- lows the Workframe to identify which executables to use from any given project. The Eyecatcher must not begin with the three letters "IBM", this prefix is reserved by IBM. f. In the language extension input line enter ".CPP" for Borland C++. This tells the Workframe to compile all files that have the .CPP extension. g. In the Language compile input line enter "BCC.EXE" for the Borland C++ command line compiler. This is the executable IBM Workframe/2 uses for compilation. PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/16 TITLE : C++ Language News -- Volume 1 Number 11 h. In the Compile Options DLL input line enter "BC_WF.DLL". The Compiler Options DLL allows you to specify compiler options for the Borland C++ com- piler when being called by IBM Workframe/2. i. In the Language linker input line enter "TLINK.EXE" for the Borland C++ command line linker. This is the executable IBM Workframe/2 shells to for link- ing. j. In the Linker Options DLL enter "TLINK_WF". The Linker Options DLL allows you to specify linker options for the Borland C++ linker when being called by IBM Workframe/2. k. Select the Language Variables button to set both your include and library paths for the Borland C++ compiler and linker. On a system where Borland C++ is installed on drive C:, the include path would be: "C:\BCOS2\INCLUDE" and the library path would be "C:\BCOS2\LIB". Click the OK Button to return the Language Profile Management dialog box when finished. l. Click the OK button on the Language Profile Man- agement dialog box. Borland C++ for OS/2 is now installed in the IBM Work- frame/2. You can change any of the Borland C++ compiler options through the menu choice OPTIONS under compiler options, and you can change any of the Borland C++ linker options through menu choice OPTIONS and menu item linker options when a project is open in IBM Workframe. WINDOWS 1. To enable all ObjectWindows applications to share the same copy of the ObjectWindows library, you can dynamically link them to the OWL DLL. To do this, be sure that the following is done: a. When compiling, by sure to define the macro _CLASSDLL on the command-line (to the command-line compiler) or in the IDE (Options|Compiler|Code Generation). b. If using any memory model other than the large memory model, ensure that all pointers and references in class data members, arguments list, and return values use the PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/16 TITLE : C++ Language News -- Volume 1 Number 11 _FAR macro to force far addresses. c. Instead of specifying static linking of the Object- Windows library (OWLWS.LIB, OWLWM.LIB, or OWLWL.LIB), specify the ObjectWindows DLL import library (OWL.LIB). TURBO VISION 1. The version of Turbo Vision which comes with the Borland C++ 3.1 package (version 1.03) seems very slow. Why? The problem is twofold. One, snow-checking was inadvertent- ly turned on by default, and two, the new operator that is in TV.LIB has debugging code compiled in with it. Since Borland C++ 3.1 with Application Frameworks has the source code for Turbo Vision available, follow the following in- structions to correct these problems. a. To fix the snow-checking problem, change to the \TVISION\SOURCE directory and edit the file TSCREEN.CPP. Change line 42 from -- Boolean near TScreen::checkSnow = True; to -- Boolean near TScreen::checkSnow = False; Recompile the module with the following command-line. You may need to make adjustments to the include direc- tory path for the compiler to find TV.LIB (see p. 172 and pp. 174-177 of the Borland C++ _User's Guide_ for further details). bcc -c -P -O1 -ml tscreen.cpp tlib /0 ..\lib\tv.lib -+ tscreen.obj This will compile TSCREEN.CPP and add it to the Turbo Vision library for later use. If you only installed the IDE, you can rebuild this module by setting the following options and compiling TSCREEN.CPP. Once the module has been compiled, execute the TLIB command shown above. PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/16 TITLE : C++ Language News -- Volume 1 Number 11 Options|Compiler|Optimizations: -- set to smallest code. Options|Compiler|Code Generation: -- set for large memory model. Options|Directories: -- set the correct include paths. b. To remove the debugging code from the new operator, you only need to recompile the source for that module. Re- compile NEW.CPP with the following command-line. As mentioned previously, you may need to adjust the include path used by the compiler. bcc -c -P -O1 -ml -DNDEBUG new.cpp tlib /0 ..\lib\tv.lib -+ new.obj The -DNDEBUG defines the NDEBUG symbol and will cause compilation of NEW.CPP without the extra debugging code. If you only installed the IDE, set the options listed above, and also set the following. Once the moudle has been compiled, execute the TLIB command shown above. Options|Compiler|Code Generation|Defines: -- add NDEBUG to this line. Upon performing these two steps, the Turbo Vision lib- rary should again be performing at the speed it had in previous versions. PARADOX ENGINE 1. The generate utility will create a custom record from either a Paradox table or from a special map file, described in the HELPME.TXT file. The most important uses for custom records are to allow the access of fields without having to use the GetField()/PutField() functions, and it allows the use of "virtual" fields. Virtual fields can only be created from a custom record map file, where you can match any number of fields from the table to a variable to be used in the custom record. 2. It is possible that the nulVec data member is not set cor- PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 12/16 TITLE : C++ Language News -- Volume 1 Number 11 rectly. Use the 'Custom::isNull' member function to test if the custom record is treating a gvenin field as Null. The clear() member function sets all fields to Null, so you might want to add your own function unClear() to mark all fields for usage. PASCAL 1. The following unit extends the maximum number of files that can be open simultaneously from 20 to 255. Files in DOS 2.0 or later are controlled by file handles. The number of file handles available to application programs is controlled by the FILES environment variable stored in CONFIG.SYS. If no FILES variable is established in CONFIG.SYS, then only eight file handles are made available. However, DOS requires five file handles for its own use (controlling devices such as CON, AUX, PRN, etc.). This would leave only three handles for use by application programs. The unit extend, described below resizes the amount of allo- cated memory for a Turbo Pascal program to allow space for new file handles. In doing so, it also resizes the heap by adjusting the value of FreePtr, the pointer used in FreeList management. Since the FreeList is being manipulated, the heap must be empty when the extend unit is initialized. This can be guaranteed by including extend as one of the first units in your program's "uses" statement. If any heap has been allocated when extend initializes, the program will halt with an error message. Before using Extend, you must specify a FILES variable in CONFIG.SYS file such as the following: FILES=255 The unit Extend is set up for 255 file handles. You can re- duce this number by changing the Handles constant in the u- nit's source code and reducing the number specified for the FILES environment variable. unit Extend; {This extends the number of FILE handles from 20 to 255} PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 13/16 TITLE : C++ Language News -- Volume 1 Number 11 {DOS requires 5 for itself. Applications can use up to} {250} interface implementation uses dos; const Handles = 255; {You can reduce the value passed to Handles if fewer} {files are required.} var reg:registers; begin {Check the Dos Version} {This technique only works for DOS 3.0 or later} reg.ah:=$30; MsDos(reg); if reg.al<3 then begin writeln('Extend Unit require DOS 3.0 or higher'); halt(1); end; {Reset the FreePtr} {This reduces the heap space used by Turbo Pascal} if HeapOrg<>HeapPtr then {Checks to see if the Heap is empty} begin write('Heap must be empty before Extend starts'); writeln; halt(1); end; HeapEnd:= ptr(Seg(HeapEnd^) - (Handles div 8 +1), Ofs(HeapEnd^)); {Determine how much memory is allocated to program} {Reg.Bx will return how many paragraphs used by pro-} {gram} reg.ah:=$4A; reg.es:=PrefixSeg; reg.bx:=$FFFF; PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 14/16 TITLE : C++ Language News -- Volume 1 Number 11 msdos(reg); {Set the program size to the allow for new handles} reg.ah:=$4A; reg.es:=PrefixSeg; reg.bx:=reg.bx-(Handles div 8 +1); msdos(reg); {Error when a Block Size is not appropriate} if (reg.flags and 1)=1 then begin Writeln('Runtime Error ',reg.ax); Writeln('In the Extend Unit'); halt(1); end; {Allocate Space for Additional Handles} reg.ah:=$67; reg.bx:=Handles; MsDos(reg); end. Write the following program to a separate file. This pro- gram tests the Extend unit. This test should be done on systems equipped with a hard disk. program TestEx; uses Extend; type filearray=array[1..250] of text; var f:^filearray; i:integer; s:string; begin {Allocate Space for fILE Variable Table} new(f); {oPEN 250 files simultaneously} for i:=1 to 250 do begin str(i,s); PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 15/16 TITLE : C++ Language News -- Volume 1 Number 11 Assign(f^[i],'Dum'+s+'.txt'); rewrite(f^[i]); writeln('Open #',s); end; {Write some text to the files} for i:=1 to 250 do write(f^[i],'This is a test file'); {Close the files} for i:=1 to 250 do begin close(f^[i]); writeln('Closing #',i); end; {Erase the files} for i:=1 to 250 do begin erase(f^[i]); writeln('Erasing #',i); end; end. QUIZ Last week's quiz question focused on the initialization of a static member within a template class. Here is one pos- sible solution: #include template class foo { public: static struct bar { unsigned c; int i; } b; }; template ostream& PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 16/16 TITLE : C++ Language News -- Volume 1 Number 11 operator<<(ostream_withassign &out, foo &f) { return out << "(" << f.b.c << ", " << f.b.i << ")"; } foo::bar foo::b = { 0, 1 }; main() { foo f, g; cout << f << endl; cout << g << endl; return 0; } For this week's question, given a derived class and a base class which both declare copy constructors, write the de- rived class's copy constructor such that it will also call the base class's copy constructor. NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1534 How to create custom bitmaps for but- tons. 1533 Example of using a variable number of function arguments. 1532 Continuously updating a message box under Turbo Vision. 1400 Converting between Microsoft's binary numeric format and the IEEE standard. 1399 Using Borland C++ for OS/2 with IBM's Workframe/2. 1398 Conflicts between tools.h++ from Rogue Wave and Turbo Vision. 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 PRODUCT : Borland C++ NUMBER : 8510 VERSION : All OS : All DATE : October 25, 1993 PAGE : 17/17 TITLE : C++ Language News -- Volume 1 Number 11 you received with the Borland product to which this information pertains.