PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/13 TITLE : C++ Language News - Volume 1 Number 13 _Borland Language Support News_ ---------------------------------------------------------------- volume 1, number 13 August 27, 1993 ---------------------------------------------------------------- ABSTRACTING STRING CONCATENATION: One question which does reoccur is how can string concate- nation similar to BASIC be done in C++? A definitive ans- wer does not exist, but the following is one possible idea. String concatenation is also a good exercise in class de- sign. Several assumptions are made in the following de- sign proposed, such that they will be explained first. Looking first at concatenation from memory's standpoint, the statement a = b + c + d; where a, b, c, & d are all defined as character strings, b + c should allocate from the heap sufficient memory to contain both string b and c. b is then copied into this new location followed by append- ing or concatenating string c via the standard runtime function strcat(). Concatenating d onto this string would first require that enough heapspace to contain the (b + c) string + d is allocated followed by copying (b + c) and then append d itself. The (b + c) temporary string can then be deleted. Note that this temporary string is truly a string, but it is designated 'temporary' since its role has been fulfilled. Trying to forsee where temporary strings may need to be deleted sooner than what C++ spec- ifies (see section 12.2 of the ARM), the following design will delete the needed heapspace immediately after usage. Before introducing temporary strings and all of it assoc- iated member functions, the basic string class will be presented. Of note, the (const char*) conversion will be implemented in the string class, such that any runtime library string routine needing a const char* can deal di- rectly upon any string instance. Assignment overloading will be implemented for both string instantiations and character strings alike. The following program demon- strates this functionality: #include #include PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/13 TITLE : C++ Language News - Volume 1 Number 13 #include class string { char *s; public: string() { s = 0; } string(const char*); string(const string&); operator const char*() { return s; } operator const char*() const { return s; } string& operator=(const char*); string& operator=(const string&); ~string() { if (s) delete [] s; } }; string::string(const char *p) { s = strdup(p); assert(s != 0); } string::string(const string &r) { s = strdup(r); assert(s != 0); } string& string::operator=(const string &r) { if (s) delete [] s; s = strdup(r); assert(s != 0); return *this; } string& string::operator=(const char *p) { if (s) delete [] s; s = strdup(p); assert(s != 0); return *this; } main() { string a("foo"), b = "bar"; { string c, d = a; c = b; cout << "c = " << c << endl; cout << "d = " << d << endl; PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/13 TITLE : C++ Language News - Volume 1 Number 13 } cout << "a = " << a << endl; cout << "b = " << b << endl; return 0; } Adding the functionality of concatenation will be simple. The temporary strings discussed previously will be imple- mented as a nested class within the string class since no one needs to be aware of it externally. #include #include #include class string { class tmp { char *p; public: tmp(char *q) : p(q) { } operator char*() { return p; } operator char*() const { return p; } tmp operator+(const char*); tmp operator+(const tmp&); tmp operator+(const string&); }; char *s; public: string() { s = 0; } string(const char*); string(const string&); operator const char*() { return s; } operator const char*() const { return s; } string& operator=(const char*); string& operator=(string::tmp&); string& operator=(const string&); string::tmp operator+(const char*); string::tmp operator+(const string&); ~string() { if (s) delete [] s; } }; string::tmp string::tmp::operator+(const char *q) { char *const s = new char[strlen(p) + strlen(q) + 1]; assert(s != 0); PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/13 TITLE : C++ Language News - Volume 1 Number 13 strcpy(s, p); strcat(s, q); delete [] p; p = s; return *this; } string::tmp string::tmp::operator+(const string::tmp &t) { char *const s = new char[strlen(p) + strlen(t) + 1]; assert(s != 0); strcpy(s, p); strcat(s, t); delete [] p; p = s; return *this; } string::tmp string::tmp::operator+(const string &r) { char *const q = new char[strlen(p) + strlen(r) + 1]; assert(q != 0); strcpy(q, p); strcat(q, r); delete [] p; p = q; return *this; } string::string(const char *p) { s = strdup(p); assert(s != 0); } string::string(const string &r) { s = strdup(r); assert(s != 0); } string& string::operator=(string::tmp &t) { if (s) delete [] s; s = t; return *this; } string& string::operator=(const string &r) { PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/13 TITLE : C++ Language News - Volume 1 Number 13 if (s) delete [] s; s = strdup(r); assert(s != 0); return *this; } string& string::operator=(const char *p) { if (s) delete [] s; s = strdup(p); assert(s != 0); return *this; } string::tmp string::operator+(const char *p) { char *const q = new char[strlen(s) + strlen(p) + 1]; assert(q != 0); strcpy(q, s); strcat(q, p); return q; } string::tmp string::operator+(const string &r) { char *const q = new char[strlen(s) + strlen(r) + 1]; assert(q != 0); strcpy(q, s); strcat(q, r); return q; } main() { string a, b("Yan"), c("kee"), d("-"), e = "Doo"; a = b + c + d + e + "dle"; cout << "a = " << a << endl; return 0; } HOT ISSUES There is a problem with Borland C++ for OS/2 when in- specting/evaluating/watching the members within a class. The debugger is not handling the 'this' pointer correctly. PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/13 TITLE : C++ Language News - Volume 1 Number 13 Since the 'this' pointer is always in the EDI register, a class instance can be inspected/watched/evaluated by an explicit cast on EDI. eg. Given class foo, inspect/ watch/evaluate (foo*)EDI. DOS 1. How do I dynamically allocate a multiple-dimensioned array from the heap? The trick is in understanding the needed syntax for operator new. Note in the following code example, that in pointer's dimensioning, the first dimension is omitted where on the righthand side of the assignment, all dimensions are re- quired. #include main() { const int x = 2; // dimensions const int y = 3; const int z = 4; int (*a)[y][z] = new int[x][y][z]; for (int i = 0; i < x; ++i) // elemental assignments for (int j = 0; j < y; ++j) for (int k = 0; k < z; ++k) a[i][j][k] = i + j + k; for (i = 0; i < x; ++i) { // output array(s) for (int j = 0; j < y; ++j) { for (int k = 0; k < z; ++k) { const int w = 4;// field width cout.width(w); cout << a[i][j][k]; } cout << endl; } cout << endl; // separate submatrices } delete [] a; return 0; } PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/13 TITLE : C++ Language News - Volume 1 Number 13 OS/2 1. To give an executable total control of when a DLL is resi- dent in memory, a. When creating the DLL with Borland C++ for OS/2, use the TLINK.EXE option for /Tod. b. The application will need to manually load the DLL by using teh DosLoadModule() API function call from the Control Program Interface API. DosLoadModule() requires the name of the DLL file in order to load the DLL. c. When the DLL file is loaded, in order for the executable to call any of the functions within the DLL, the address of the function must be found with DosQueryProcAddr(). DosQueryProcAddr() takes the name of the function (as a string) and returns its address. If the address is saved in a function pointer, the function can be invoked via the function pointer. ie. // load the DLL DosLoadModule(errorBuf, sizeof(errorBuf), "MyDLL", &hmod); // copy the address of the function into 'pf' DosQueryProcAddr(hmod, 0, "MyDLLfunction", (PPFN) &pf); // call the function via the function pointer pf(); d. This method of loading the DLL file allows the execu- table to unload the DLL after the appropriate functions within the DLL have been called. This can be done via DosFreeModule(). ie. DosFreeModule(hmod); Be aware that if other executables use the same DLL unloaded here, then the DLL may still be in memory even if DosFreeModule() is called. WINDOWS 1. What are the SALLOCWx.LIB libraries in ObjectWindows, and PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/13 TITLE : C++ Language News - Volume 1 Number 13 what to they do? OWL includes a suballocation package. The SALLOCWx.LIB files redefine farmalloc(), farfree(), and farcalloc() to suballocate global memory blocks rather than map di- rectly onto GlobalAlloc()/GlobalLock(). Operator new in the medium model does a near allocation (and uses malloc() rather than farmalloc()). However, malloc() maps onto LocalAlloc()/LocalFree() (which by definition is suballocating global memory blocks). In the large data models, operator new calls farmalloc(), so with the suballocator, these blocks are suballocated. Without it, they just map onto GlobalAlloc()/GlobalLock(). One main benefit from suballocation is that it will reduce the number of handles used in allocating memory. By just calling GlobalAlloc(), you will use a handle for each allocation. By suballocating, you will use less handles. Windows' Enhanced Mode has 8k handles available and Stan- dard Mode has 4k handles available. TURBO VISION 1. Why does my OK button work with a modal dialog box, but not when I make that dialog box modeless? My button should be sending the cmOK message. When a dialog box is modal (ie. a call to execView() has been made), the cmOK message is trapped by the handleEvent() member function of TDialog and initiates the destruction of the dialog box through a call to endModal(). When the dialog box is modeless (ie. a call to insert() has been made), TDialog does not trap this since it is not obvious what functionality cmOK should have in a modeless situa- tion. To get an OK button which sends cmOK to work in a modeless dialog, it is necessary to derive a new class from TDialog. The code fragment below gives the relevant details. class MyDialog : public TDialog { PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/13 TITLE : C++ Language News - Volume 1 Number 13 public: MyDialog(const TRect&, const char*); td(const TRect&, const char*); // ... virtual void handleEvent(TEvent&); }; MyDialog::MyDialog(const TRect &bounds, const char *aTitle) : TDialog(bounds, aTitle), TWindowInit(&td::initFrame) { } void MyDialog::handleEvent(TEvent &event) { TDialog::handleEvent(event); if (event.what == evCommand) { switch (event.message.command) { case cmOK: shutDown(); break; default: return; } } clearEvent(event); } Something like this should appear somewhere in your code to actually use the above class: TRect rect(0, 0, 60, 13); rect.move(random(39), random(10)); MyDialog *dialog = new MyDialog(rect, "modeless"); dialog->insert(new TButton(TRect(15, 10, 25, 12), "~O~kay", cmOK, bfDefault)); deskTop->insert(dialog); PARADOX ENGINE 1. Why am I having problems saving records to a table using custom records? It is possible that the nulVec data member is not set cor- rectly. Use the 'Custom::isNull()' member function to test whether the custom record is treating a given field as Null. PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/13 TITLE : C++ Language News - Volume 1 Number 13 The 'clear()' member function sets all fields to Null, so a new function 'unClear()' may be desired to mark all fields as being used. 2. Will the Database Frameworks work within a DLL? Yes. Obtain the file PX30P2.ZIP from either our DLBBS at (408)431-5096 or CompuServe for information about the re- quired changes to use the Database Frameworks in a DLL. NOTE: the workaround prescribed only works when using the Database Frameworks with Borland C++. PASCAL 1. The following unit will intercept hardware interrupt $9 and check for a Ctrl-Break. If the interrupt does not contain a Ctrl-Break the preceding Int9 is called. If Ctrl-Break was pressed we call Turbo's int23 handler to stop execution of the program, while still executing the ExitProcs. Unit BreakNow; interface Uses DOS,CRT; { No interface } implementation Var Int23, OldExit, OldKBD : pointer; { This procedure will jump from an ISR to the ISR vector passed } procedure JmpOldISR(OldISR: pointer); inline($5B/$58/$87/$5E/$0E/$87/$46/$10/$89/ $EC/$5D/$07/$1F/$5F/$5E/$5A/$59/$CB); {$F+} procedure Key_ISR( Flags, CS, IP, AX, BX, CX, DX, SI, PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/13 TITLE : C++ Language News - Volume 1 Number 13 DI, DS, ES, BP : word ); interrupt; Begin if CheckBreak and ((Mem[0000:$0417] and 4) = 4) and (Port[$60] = 70) then Begin inline($E4/$61/$8A/$E0/$0C/$80/$E6/$61/ $86/$E0/$E6/$61/$B0/$20/$E6/$20); JmpOldISR( Int23 ); {Jump to cur Int23 handler} end; JmpOldISR( OldKBD ); {Jump to Old handler} End; Procedure Exitit; Begin ExitProc := OldExit; { Restore Old ExitProc } SetIntVec( 9, OldKBD ); { Restore Old Handler } end; {$F-} Begin OldExit := ExitProc; { Save old ExitProc } ExitProc := @Exitit; { Set new ExitProc } GetIntVec( $23, Int23 ); { Current Int23 vector } GetIntVec( 9, OldKBD ); { Current Int9 vector } SetIntVec( 9, @Key_ISR ); { Set Int9 to new routine } end. QUIZ Last week's quiz asked for code which would allow public usage of private member functions. Here is one possible example: #include class foo { void (foo::*mfp)(const int); void f(const int i) { cout << "foo::f(" << i << ")" << endl; } void g(const int i) { cout << "foo::g(" << i << ")" << endl; PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 12/13 TITLE : C++ Language News - Volume 1 Number 13 } public: foo(const int); void member(const int i) { (this->*mfp)(i); } }; foo::foo(const int i) { switch (i) { case 0: mfp = &foo::f; break; default: mfp = &foo::g; break; } } main() { foo f(0), g(1); f.member(2); g.member(4); return 0; } This week's quiz question is actually an exercise. Develop a class 'foo' where: foo i = 0, j = 0; j = i + 15 + 2 + 1; means that 'i' is 'or'ed with bit 15 and bit 2 and bit 1. NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1546 How to check the printer's status. 1545 Three example manipulators (binary, pointer, & comma). 1544 Creating an ostream manipulator. 1543 Class manipulator example. 1542 Using makefiles with paths for imp- licit rules. PRODUCT : Borland Languages NUMBER : 8512 VERSION : All OS : All DATE : October 25, 1993 PAGE : 13/13 TITLE : C++ Language News - Volume 1 Number 13 1541 Using a cmReleasedFocus message to update a dialog box. 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.