PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/11 TITLE : C++ Language News -- Volume 1 Number 10 _Borland Language Support News_ ---------------------------------------------------------------- volume 1, number 10 July 12, 1993 ---------------------------------------------------------------- MIGRATING TO AN OBJECT-ORIENTED MINDSET III In the previous two installments to this series, a stack template class has been developed to implement the common LIFO data structure. No means of iterating through the link- ed list has been created which would be nondestructive to the list. The overhead of placing this into the class proper may be considered to be advantageous, but in the course of development, a programmer might like to peek occasionally in- to the contents. This article will look at one possible so- lution. Iterators which are separate from the class iterated upon require careful use. If insertion into a structure occurs after the iterator has been instantiated, the iterator may be referencing a structure which is no longer valid. As an example, consider a class which abstracts an array which can resize itself. An iterator upon this class would prob- ably look at the base address of the array, and increment some internal pointer down the array. For an array to "ex- pand" itself, heapspace sufficient in size to the current array plus whatever increment (delta) that the array is to grow would require allocation. Each element would then be sequentially copied into the new array. In the scenario where an iterator had been instantiated before the resizing, the iterator base pointer would be pointing to the location of the old array as opposed to the new. This is a common problem with iterator design. The approach used here with the stack class's iterator will try to minimize the need for knowing the address of the root node. In particular, a reference to the overall stack in- stance will be maintained. This will get away from some problems, but this design is still plagued with the problems previously discussed with creating an iterator instance prior to altering the stack. Given the previous stack class implementation (note the addi- PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/11 TITLE : C++ Language News -- Volume 1 Number 10 tion of class friendship and the size() member function with- in the stack template class): template class node { friend class stack; friend class iter; T value; node *next; node(const T &t) : value(t) { next = 0; } }; template class stack { friend class iter; node *root; unsigned n; public: stack() { n = 0; root = 0; } int operator<<(const T&); // push analog int operator>>(T&); // pop analog unsigned size() { return n; } unsigned size() const { return n; } ~stack(); }; template int stack::operator<<(const T &t) { node *p = new node(t); if (!p) return 1; if (root) { p->next = root; root = p; } else root = p; ++n; return 0; } template int stack::operator>>(T &t) { if (!root) PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/11 TITLE : C++ Language News -- Volume 1 Number 10 return 1; t = root->value; node *p = root->next; delete root; root = p; --n; return 0; } template stack::~stack() { while (root) { T t; *this >> t; } } The iterator designed here will be expected to nondestruc- tively return a reference to the value within the list fol- lowed by incrementing itself to the next node of the linked list. This necessitates the friendship sent in the above node and stack class definitions. The iterator's specifi- cation also indicates what members will need to be retained within the class -- a reference to the stack iterated upon and a pointer which will be incremented down the list. Here is one potential solution: 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()(); }; The reset() and size() member functions are added-on func- tionality. reset() will allow the programmer to reset the internal pointer 'p' at will. As for the implementation of the iterating overloaded '()' operator, the following points out where this design suffers: PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/11 TITLE : C++ Language News -- Volume 1 Number 10 template T& iter::operator()() { node *q = p; if (p->next) p = p->next; else reset(); return q->value; } Depending upon where a push or pop from the stack occurs, 'p' could contain an invalid address. This could be coded around, but then efficiency would suffer. As always, ro- bustness and efficiency often are opposing forces. The following driving function provides a simple implemen- tation of the above template code: main() { const int n = 4; stack stk; for (int i = 0; i < n; ++i) stk << i; iter it(stk); for (i = 0; i < 2 * it.size(); ++i) { int j = it(); const int w = 4; // field width cout.width(w); cout << j; } cout << endl; return 0; } Next week's issue will bring this series to a close. It will focus on cleaning this design up and tying up other loose threads. HOT ISSUES Protogen 2.2 shipping with Turbo C++ for Windows 3.1, Visual PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/11 TITLE : C++ Language News -- Volume 1 Number 10 Edition has a problem with Resource Workshop when a button is added after inserting a static text control. PVUPDATE. ZIP on the DLBBS at (408)439-9096 contains two DLL's which are to be copied over to the Protogen installation disks prior to reinstallation which will correct this problem. This same file is available on CompuServe as PVUPD8.ZIP in LIB-2 of BCPPWIN. DOS 1. How do I determine the size of the available stack at run- time? In the near memory models (tiny, small, & medium), the stack resides in the data segment beginning at the end of DGROUP, and grows down in memory towards SS and DS. Since __brklvl always points to the top of the heap, the stack is allowed to grow until SP reaches __brklvl (when a stack overflow message will be generated if stack checking is enabled). In the far memory models (compact, large, and huge), the stack begin prior to _heapbase and grows downward to SS. #include unsigned stack(); main() { cout << "stack size = " << stack() << endl; return 0; } unsigned stack() { unsigned long t; extern unsigned long _heapbase; // far heap extern unsigned __brklvl; // near heap #if defined(__COMPACT__) || defined(__LARGE__) || \ defined(__HUGE__) t = 16 * (*((unsigned*) _heapbase + 1) - _SS) - 16; #else t = *((unsigned*) _heapbase + 1) - __brklvl - 1; #endif return t; } PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/11 TITLE : C++ Language News -- Volume 1 Number 10 The Technical Information document TI1036 goes into more de- tail on utilization and should be obtained for more informa- tion. OS/2 1. How do I call my 16-bit DLL from an application build with Borland's compiler? Here is what is needed for prototypes of the DLL's func- tions: 1. 16-bit functions taking/returning an int should be prototyped as taking/returning a short, since 16-bit int's are now considered 'short' (USHORT or APIRET16 are the typedefs in OS2DEF.H) 2. The function should be declared as APIENTRY16, or _far16 _pascal 3. Any pointers that the function takes as parameters must be declared as _far16 eg. Former prototype in FOO.H (FOO.DLL's associated header file): int FooData(char *, int); New prototype for linking 32-16 (in your BC++ program): short _far16 _pascal FooData(char _far16 *, short); NOTE: Don't use ... char * _far16 ... It is easy to make that mistake since IBM's pointers are declared with _Seg16 after the '*', e.g. char * _Seg16. If the types declared in OS2DEF.H are desired, it would be: APIRET16 APIENTRY16 FooData(PCHAR16, SHORT); PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/11 TITLE : C++ Language News -- Volume 1 Number 10 The compiler will take care of the address conversions for the function and the data being passed. NOTES: Most DLL's need the _pascal calling convention. Sybase is an exception, Sybase DLL functions need to be called as _cdecl. It's difficult to pass a 32-bit function pointer to be exe- cuted from a 16-bit DLL ("callback"). See \BCOS2\EXAMPLES\THUNK for more information. Functions in a 16-bit DLL that take variable argument lists (eg. printf() family) are unworkable. The compiler cannot know to convert their addresses. An array of pointers cannot be converted for the same reason. WINDOWS 1. Microsoft has removed the keyword INDEX from the Windows 3.1 help compiler. The CONTENTS option is equivalent to the INDEX option that was available in Windows v3.0. 2. When are child windows and controls created in ObjectWindows? TWindowsObject::SetupWindow() is responsible for actually creating the child windows and controls of a window. 3. What is a DDVT, and how is it used in ObjectWindows? DDVT's (dynamic dispatchable virtual tables) allow 'tagging' a member function with an integer ID. In ObjectWindows, member functions are associated with the Windows message in which they respond. OWL's dynamic dispatching mechanism then routes control to that member function when the message comes in. DDVT's eliminate the need for a lengthy switch statement which would provide the same functionality. TURBO VISION PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/11 TITLE : C++ Language News -- Volume 1 Number 10 1. Why won't my mouse go down to the bottom of the screen in Turbo Vision? Some older mouse drivers have trouble going to the bottom of the screen when in character modes that support addition- al lines on the screen. Possibly the best solution is to contact the supplier of the mouse and get a newer version of the their driver. A second solution may be to force the range of the mouse by adding -- #define Uses_TMouse; #define Uses_TScreen; before the #include line, and -- TMouse::setRange(TScreen::screenWidth - 1, TScreen::screenHeight - 1); within function main(). 2. Why does aMax decrease when the window grows for TScrollBar? Given the situation where a list of 20 items is being dis- played in a view which is only 10 records high, then 20 - 10 is a need to scroll 10 records. If the view is resized to display 15 records, then 20 - 15 = 5 records to scroll. This is why aMax = maximum number of items - number of dis- playable items. PARADOX ENGINE 1. Can the Paradox Engine be used with a DOS Extender? The Paradox Engine does not support protected mode, so En- gine function calls cannot be made from an extender. The Paradox Engine does support the extended memory usage when used with Borland Pascal 7.0. 2. Why am I experiencing conflicts with the locking mechanism? The user name is the same for all Engine applications. Version 3.0 of the Paradox Engine will not determine the PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/11 TITLE : C++ Language News -- Volume 1 Number 10 user name from the network, while version 2.0 did. This allows the Engine to be more generic. This implies that all Engine applications will default to the same user name, pxEngine, unless the user name has been explicitly set. This may cause conflicts with locks in a DOS appli- cation, because the locking protocol users the user name to keep track of who owns a lock. Set the user name using the third parameter to PXNetInit() in the DOS environment, or using the PXENGCFG.EXE utility in the Windows environ- ment. PASCAL 1. How can I determine the format of a Turbo Pascal real variable? The following program will provide some insight into the conversion: Program RealConv; type SixByteArray = array[1..6] of byte; var r : real; s : SixByteArray absolute r; { Allows access to individual real type bytes } i,j : byte; PosFlag : boolean; Mantissa : real; Number : real; function power (x,y : integer) : real; begin power := exp(y * ln(x)); end; begin write('Enter floating point Number '); readln(r); { Check if entry is positive from bit 7 of byte 6 } PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/11 TITLE : C++ Language News -- Volume 1 Number 10 PosFlag := ($80 and s[6]) = 0; { Force bit 7 of byte 6 on } s[6] := s[6] or $80; { Initialize the Mantissa } Mantissa := 1.0; { Check each byte of mantissa } for i := 2 to 6 do { Check each bit } for j := 0 to 7 do if ((s[i] shr j) and 1 ) = 1 then { Increment mantissa appropriately } Mantissa := Mantissa + power(2, (j + (i-2)*8)); { Normalize the number by dividing by 2^40 } Number := Mantissa / power(2,40); { Get number by multiply Mantissa by the exponent } Number := Number * power(2, s[1] - $80); if not PosFlag then Number := Number * -1; writeln(Number); readln; end. QUIZ Last week's quiz focused on the duplicate class definitions in different files where the order of virtual functions are pathologically mixed up. As given, the code will compile (with warning about the lack of return values) and execute as expected. The trick is to understand what happens when the virtual table is created. In B.CPP, the virtual table was not needing to be constructed since no constructor calls were required. The virtual table was not required/created until A.CPP where the dynamic instance of class A forced the compiler to generate the appropriate code. For this week's quiz, how would the following static array be initialized? template class foo { PRODUCT : Borland C++ NUMBER : 8509 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/11 TITLE : C++ Language News -- Volume 1 Number 10 public: static struct bar { unsigned c; int i; } b; }; NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1393 Common issues encountered when using Turbo C++ & Borland C++ with MS-DOS 6.0. 1392 How to set a breakpoint within a DLL. 1391 How to implement shared memory in a OS/2 DLL. 1385 Tutorial on streams, part 3/7. 1384 Tutorial on streams, part 2/7. 1035 Overview of the requirements needed when using the template version of the class library. 649 Possible coding errors which may lead to general-protection faults. 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.