PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/12 TITLE : C++ Language News - Volume 1 Number 7 _Borland Language Support News_ ---------------------------------------------------------------- volume 1, number 7 June 7, 1993 ---------------------------------------------------------------- USING QUICKSORT WITH C & C++ Quicksort has become accepted to be one of the most robust and fastest sorting algorithms known at this time. Because of the prevalence of implementation and usage, it has become part of the standard ANSI C runtime library. As such, it can be simply used in an application such as -- #include #include #define MAX 3 struct foo { double v; }; int ascend(const void*, const void*); void print(struct foo*); main() { struct foo a[MAX]; a[0].v = 0.0; a[1].v = -0.02; a[2].v = 0.01; print(a); qsort((void*) a, MAX, sizeof(a[0]), ascend); print(a); return 0; } int ascend(const void *p, const void *q) { double v = ((struct foo*)p)->v - ((struct foo*)q)->v; if (v < 0.0) return -1; else if (v > 0.0) return 1; else return 0; } void print(struct foo *a) { PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/12 TITLE : C++ Language News - Volume 1 Number 7 int i; printf("["); for (i = 0; i < MAX; i++) printf(" %6.2lf", a[i].v); printf(" ]\n"); } The question is often asked how can qsort() be used in C++. Since classes and structures within C++ are closely related, the use of qsort() with classes is similar to the above C application. Note that ordinary member functions cannot be used as the sorting function specified as the last argument to qsort(); this is due to member functions implicitly passing the 'this' pointer on the stack which is not expected by qsort(). This limits the comparison function passed to qsort() to regular functions or static member functions. Quicksort can also be used in situations where multiple in- heritance exists. The following example demonstrates one method of how this can be done -- #include #include #include class fruit { public: virtual char* name() const { return "fruit"; } }; class apple : public fruit { public: char* name() const { return "apple"; } }; class banana : public fruit { public: char* name() const { return "banana"; } }; class orange : public fruit { public: char* name() const { return "orange"; } }; void print(const fruit *p[], const int n) { cout << "("; for (int i = 0; i < n; ++i) PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/12 TITLE : C++ Language News - Volume 1 Number 7 cout << " " << p[i]->name(); cout << " )" << endl; } int compare(const void *p, const void *q) { return strcmp((*(fruit**)p)->name(), (*(fruit**)q)->name()); } main() { const int max = 3; fruit *p[max]; apple a; banana b; orange o; p[0] = &b; p[1] = &o; p[2] = &a; print(p, max); qsort((void*)p, max, sizeof(fruit*), compare); print(p, max); return 0; } Quicksort can thus be used in C++ programs as long as some method can be determined of how to compare dissimilar ob- jects which can be derived from a common ancestor. HOT ISSUES Turbo C++ 3.0 incorrectly deals with the increment operator, ++, and huge pointers. The following code illustrates the problem and gives the simple workaround. #include main() { const long max = 95000L; static long huge a[max]; cout << "assigning elements..." << endl; for (long i = 0; i < max; ++i) a[i] = i; cout << "verifying all array elements..." << endl; long huge *p = a; for (i = 0; i < max; ++i) { if (*p != i) cerr << "error in a[" << i << "]" << endl; // ++p; // errant line of code p = p + 1; // workaround PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/12 TITLE : C++ Language News - Volume 1 Number 7 } cout << "done" << endl; return 0; } The line that is commented out demonstrates the problem. If this line is uncommented, and the following line is comment- ed out, then incorrect program execution will be seen after compilation and linking. If the code is compiled and linked as seen above, execution will proceed as expected. DOS 1. Values such as '-a' and '-b' passed to a program via com- mand-line arguments are seen by the compiler as literal values (in this case, 0x612d and 0x622d respectively). By casting *argv as a pointer to an integer, determining the value of a command-line switch can be done faster in fewer lines of code. eg. #include main(int argc, char **argv) { while (--argc && **argv++) { cout << "command-line switch = "; switch (*(int*) *argv) { case '-a': case '-A': case '/a': case '/A': cout << "-a, -A, /a, or /A" << endl; break; case '-b': case '-B': case '/b': case '/B': cout << "-b, -B, /b, or /B" << endl; break; default: cout << *argv << "(unknown)" << endl; break; } } return 0; } PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/12 TITLE : C++ Language News - Volume 1 Number 7 OS/2 1. The following function will track the splitbar in detail view (object information) using the keyboard. This func- tion could be called as a result of a menu item selected by the user. VOID TrackSplitbar (HWND hwndCnr) { TRACKINFO ti; RECTL rclDV, rclCnr, rclCnrTitle; CNRINFO CnrInfo; /* Query the CnrInfo structure from the container. From it, get the current position of the splitbar. */ WinSendMsg(hwndCnr, CM_QUERYCNRINFO, MPFROMP(&CnrInfo), MPFROMSHORT(sizeof(CNRINFO))); /* Get the window rectangles for the container title window, the left details view window, and the con- tainer window itself. */ WinQueryWindowRect(WinWindowFromID(hwndCnr, CID_CNRTITLEWND), &rclCnrTitle); WinQueryWindowRect(WinWindowFromID(hwndCnr, CID_LEFTDVWND), &rclDV); WinQueryWindowRect(hwndCnr, &rclCnr); /* Initialize the TrackInfo structure. The splitbar has the width of a SizeBorder, and has the height of the container window minus the container title window. */ ti.cxBorder = (SHORT) WinQuerySysValue(HWND_DESKTOP, SV_CXSIZEBORDER); ti.cyBorder = (SHORT) (rclCnr.yTop - rclCnrTitle.yTop); ti.cxGrid = (SHORT) rclCnr.xRight; ti.cyGrid = (SHORT) rclCnr.yTop; /* Set the x Keyboard increment to be the number of pels you want the splitbar to move when the user presses the right and left arrow keys. Set the y Keyboard PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/12 TITLE : C++ Language News - Volume 1 Number 7 increment to 0. */ ti.cxKeyboard = 10; ti.cyKeyboard = 0; /* Set up the rectangle that is to be tracked. This is the rectangle of the splitbar. */ ti.rclTrack.xLeft = CnrInfo.xVertSplitbar; ti.rclTrack.xRight = ti.rclTrack.xLeft + ti.cxBorder; ti.rclTrack.yBottom = rclCnr.yBottom; ti.rclTrack.yTop = ti.cyBorder; /* Set up the absolute boundary rectangle. This is the rectangle specifying the boundary which the tracking rectangle cannot exceed. Set it to be the left, right, and bottom of the container, and the top of the splitbar. */ ti.rclBoundary.xLeft = rclCnr.xLeft; ti.rclBoundary.xRight = rclCnr.xRight; ti.rclBoundary.yBottom = rclCnr.yBottom; ti.rclBoundary.yTop = ti.cyBorder; ti.ptlMinTrackSize.x = ti.cxBorder; ti.ptlMinTrackSize.y = ti.cyBorder; ti.ptlMaxTrackSize.x = ti.cxBorder; ti.ptlMaxTrackSize.y = ti.cyBorder; /* Set the track flags. TF_SETPOINTERPOS will put the mouse pointer in the middle of the tracking rectangle (splitbar). */ ti.fs = TF_MOVE | TF_ALLINBOUNDARY | TF_SETPOINTERPOS; /* If the tracking is successful, inform the container of the new splitbar position. */ if (WinTrackRect(hwndCnr, NULL, &ti)) { /* Inform the container of the new splitbar position. */ CnrInfo.xVertSplitbar = ti.rclTrack.xLeft; PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/12 TITLE : C++ Language News - Volume 1 Number 7 WinSendMsg(hwndCnr, CM_SETCNRINFO, &CnrInfo, MPFROMLONG(CMA_XVERTSPLITBAR)); } return; } WINDOWS 1. How do I associate an icon with a TWindow in OWL? Within your application, derive from TWindow, override the member function GetClassName() to return a unique name, and override the member function GetWindowClass() to load a handle to your icon. eg. class TMyWindow : public TWindow { // ... virtual void GetWindowClass(WNDCLASS&); virtual LPSTR GetClassName(); }; void TMyWindow::GetWindowClass(WNDCLASS &w) { TWindow::GetWindowClass(w); w.hIcon = LoadIcon(GetApplication()->hInstance, MAKEINTRESOURCE(ICON_MYICON)); } LPSTR TMyWindow::GetClassName() { return ("MY_WINDOW"); } TURBO VISION 1. When I compile my old Turbo Vision code that uses TFileList with Borland C++ 3.1, it produces errors on the constructor calls. When I check the STDDLG.H header file, I see that the constructor parameters have been changed. Why is this, and how do I fix it? The version of Turbo Vision shipping with Borland C++ 3.1 PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/12 TITLE : C++ Language News - Volume 1 Number 7 has a modified TFileList object. The constructor no longer accepts the aWildCard parameter in the new object. This is because of a design change allowing a TFileList object to be created without actually reading a directory. At the point where you know which directory needs to be read, you can call readDirectory() with the appropriate wildcard mask. eg. old version: // note the wildcard mask in double quotes TFileList *p = new TFileList(TRect(0, 0, 20, 10), "*.*", 0); new version: TFileList *p = new TFileList(TRect(0, 0, 20, 10), 0); p->readDirectory("*.*"); The above two segments of code are equivalent. Alterna- tively, readDirectory() doesn't have to be called immed- iately. It could be called after some user input from another event. This design change allows a delay to be made between when a TFileList is instantiated as compared to when the direct- ory read is performed. PARADOX ENGINE 1. I am getting an "Error 118: Table is busy" error message. Why? This error happens when the application tries to lock a table when the corresponding prevent lock already exists, or when the application tries to set a prevent lock and a conflicting lock already exists. Check for old, unused lock files. Delete any *.LCK files that may exist after all Paradox Engine applications have terminated. Also, run the TUTILITY program to test for table validity. See page 31 of the _Paradox User's Guide_ for more information on locks. 2. I am getting an "Error 120: Table is not found" error PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/12 TITLE : C++ Language News - Volume 1 Number 7 message. Why? Besides not being able to find the table, this error can also occur when the sort order that the Engine uses is different from the sort order of the table. A common cause of this error is using the international sort order with Paradox, and using the Engine's default ASCII sort order. The sort order can be changed by using the function PXSetDefaults(). PASCAL 1. When Pascal programmers are using BASM, they are often baffled when it comes time to address a field of an ob- ject. The issue is that the field is usually located at an offset from the segment of the pointer to that object, not from the data segment of the program. As a result, a viable way to address a field of the object is to first load its object's segment and offset into ES:DI. You can use the following line of code to do this: les di, Self Once you are properly addressing the object itself, then all you need do is calculate the offset of the field from the beginning of the object. This can be done by adding the proper number of bytes to ES:DI. For instance, if an object has three fields, all two bytes in size, then the offset of the third field would be four: mov ax, word [es:di + 4] If these kinds of calculations bother you, you can avoid them by loading the segment and offset of the object di- rectly into the data segement, but you should be sure to save its original value on the stack, with a push and a pop. A simple illustration of the above principlese would be an object with 3 fields, the last being an integer called M. This example simply moves the value of that field into the AX register: program AsmObj; PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/12 TITLE : C++ Language News - Volume 1 Number 7 type PMyObject = ^TMyObject; TMyObject = Object i,j: Word; M: Integer; procedure Foo; procedure Foo1; end; procedure TMyObject.Foo; begin asm les di, Self mov ax, word [es:di + 4] end; end; procedure TMyObject.Foo1; begin asm push ds lds di, Self mov ax, word ptr [di + M] pop ds end; end; var O: PMyObject; begin New(O); O^.M := 10; O^.Foo; O^.Foo1; Dispose(O); end. QUIZ Last week's code illustrated the common 'gotcha' associated with virtual base classes. In this case, the most derived PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/12 TITLE : C++ Language News - Volume 1 Number 7 class in an inheritance tree will initialize the virtual base class as opposed to class 'left' or 'right'. Because of this, the value '0' (the constructor's default value) will be used in the assignment of 'v' as opposed to the value '3'. This week's quiz centers about the following code: #include #include class foo { int v; public: foo(int i = 1) { v = i; cout << "foo::foo(int);" << endl; } int f() { assert(v == 1); v = 1; return v; } void display() { cout << "v = " << v << endl; } }; class bar : public foo { public: bar() : foo(f()) { } }; main() { bar b; return 0; } Why is an assert error generated? The answer will be pub- lished in next week's issue. NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1390 information on accessing Borland's Down- load Bulletin Board System (DLBBS). 1378 information on sources on SVGA BGI drivers. 1338 overview of Borland C++ for OS/2. PRODUCT : Borland C++ NUMBER : 8506 VERSION : All OS : All DATE : October 25, 1993 PAGE : 12/12 TITLE : C++ Language News - Volume 1 Number 7 806 installation & configuration issues re- lating to running either Borland or Turbo C++ on a network. 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.