PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/11 TITLE : C++ Language News -- Volume 1 Number 6 _Borland Language Support News_ ---------------------------------------------------------------- volume 1, number 6 May 24, 1993 ---------------------------------------------------------------- USING TEMPLATE CODE AND INHERITANCE ACROSS MULTIPLE FILES If the source file to template classes are spread across numer- ous files, the compiler switches -Jgd and -Jgx will be required in order to link the common definitions typically compiled into the code. The -Jgd switch will make all common definitions pub- lic which are needed by the linker in order to resolve external symbols. Coupled into this same issue is the need for explicit code to be generated based upon the actual template arguments needed by an application. Given the scenario where the modules containing the template class source code is separated from the modules which actually use the template code, the compiler needs to know at the moment of compilation what code is to be gen- erated. This is the purpose of the typedef statements seen in the example published in the documentation. The abstraction provided by template classes contradicts the efficiency of C. Template functions and classes provide to the programmer a means of creating generic code which can later have the argument types later substituted into the code. This is con- trary to compilation which needs to have all information at the time when machine instructions are generated. As an example, the instructions needed for performing floating point addition are different than that needed by integer addition. The typedef statements mentioned earlier serve as a sentinel to tell the compiler to generate the correct code for the given type. This is necessary in the case where the actual instantiation of the template class occurs over in another source file. So applying this implementation scheme to inheritance trees, the programmer will need to be sure that typedef statements are seen for each class (base and derived) in the hierarchy. In this fashion, machine instructions will be generated for each class. The following example shows a simple inheritance tree based upon template classes. ===============8<------------------------------------------- // FILE: main.cpp PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/11 TITLE : C++ Language News -- Volume 1 Number 6 #include "header.h" #pragma option -Jgx main() { derived d(0); d.print(); return 0; } ===============8<------------------------------------------- // FILE: header.h #if !defined(HEADER_H) #define HEADER_H #include class foo { int v; public: foo(const int &i) { v = i; } void print() { cout << "(" << v << ")" << endl; } }; template class base { T v; public: base(const T &t) : v(t) { } void print(); }; template class derived : public base { public: void derived(const int i) : base(i) { } }; #endif ===============8<------------------------------------------- // FILE: template.cpp #include "header.h" #pragma option -Jgd typedef base foo_base_instance; typedef derived derived_base_instance; #pragma option -Jg PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/11 TITLE : C++ Language News -- Volume 1 Number 6 template void base::print() { cout << "base::v = "; v.print(); cout << endl; } HOT ISSUES A memory leak is present within Turbo Vision's class TResourceFile constructor. On line 67 of TRESFILE.CPP, a THeader object is allocated dynamically and never deleted. The easiest fix to this is to add the line "delete header;" as the last statement to the constructor and recreate the library. DOS 1. How can I declare a function which will return a two-dimen- sional array and a pointer to a function of that same type? The following code demonstrates a simplistic use of such a function: #include #include const int n = 3; // #elements per row const int m = 4; // #elements per column double (*init(double v))[m] { double (*p)[m] = new double[n][m]; for (int i = 0; i < n; ++i) for (int j = 0; j < m; ++j) p[i][j] = v + i * 0.1 + j * 0.01; return p; } main() { double (*(*fp)(double))[m]; // function pointer fp = init; double (*a)[m]; const double v = 2.0; // initialization value a = fp(v); PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/11 TITLE : C++ Language News -- Volume 1 Number 6 for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { const int w = 10; // element width cout.width(w); cout.setf(ios::right, ios::adjustfield); cout << a[i][j]; } cout << endl; } delete [] a; return 0; } OS/2 1. The following manipulator (useful in both DOS and OS/2) dis- plays a pointer in "xxxx:xxxx" format in 16-bit (DOS) mode and "xxxxxxxx" format if in the 32-bit world (OS/2 or Win- dows NT). This is similar to what is available in the %Fp format specifier within the standard printf() function. ===============8<------------------------------------------- // FILE: iom_fptr.h #if !defined( __DOS_H ) #include // for FP_SEG, FP_OFF #endif // __DOS_H #if !defined( __IOMANIP_H ) #include // for IOSTREAM basics #endif // __IOMANIP_H #include ostream& fptr(ostream &out, void *p) { long f = out.flags(); cout.fill('0'); // 16-bit compilers display xxxx:xxxx format. #if !defined (__FLAT__) out << setw(4) << hex << FP_SEG(p) << ":" << setw(4) << FP_OFF(p); // 32-bit compilers (flat) just display 8 hex digits. #else PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/11 TITLE : C++ Language News -- Volume 1 Number 6 out << setw(8) << hex << (unsigned) p; #endif out.flags(f); return out; } /* * If 'IOMANIPdeclare' isn't defined, templates are * used. NOTE: Borland compilers use lower case * manipulator names for templates. */ #if !defined (IOMANIPdeclare) omanip fptr(void *p) { return omanip(fptr, p); } #else /* * If IOMANIPdeclare is defined, macros are used. * NOTE: when macros are used, Borland compilers * use upper case names for macros. */ #if !defined (PVOID) typedef void *PVOID; #endif IOMANIPdeclare(PVOID); OMANIP(PVOID) fptr(PVOID p) { return OMANIP(PVOID) (fptr, p); } #endif // IOMANIPdeclare // end iom_fptr.h ===============8<------------------------------------------- // FILE: main.cpp #include "iom_fptr.h" main() { char *astring="This is a string"; int anint; cout.setf(ios::uppercase); PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/11 TITLE : C++ Language News -- Volume 1 Number 6 cout << "'astring' starts at " << fptr(astring) << endl << "'anint' starts at " << fptr(&anint) << endl; return 0; } WINDOWS 1. How is the text of a button changed? Send a WM_SETTEXT message with the string for the lParam. eg. SendMessage(btnWindow, WM_SETTEXT, 0, "New Text"); If ObjectWindows is being used, this same effect can be ac- complished by using TButton::SetCaption. 2. How can the main window in OWL be maximized? When the TApplication descendent is instantiated, pass SW_MAXIMIZED instead of nCmdShow. 3. How are child windows and controls created in OWL? TWindowsObject::SetupWindow() is responsible for actually creating the child windows and controls of a window. TURBO VISION 1. Can Turbo Vision do BGI graphics? To an extent, yes. Graphics cannot be placed into text mode windows as Turbo Vision was originally designed, but full- screen graphic displays can be incorporated into a Turbo Vision application. An example can be found in \BORLANDC\TVISION\DEMOS\TVBGI.CPP. 2. How can the title of a window be changed within a Turbo Vision application? a. The easiest way is to close the window and reopen it with the new name. If more than one window is open, PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/11 TITLE : C++ Language News -- Volume 1 Number 6 however, bringing this window to the front (and gaining focus) may not be desirable. TVEDIT1.CPP contains an example of this method in TEditor::openEditor(). b. Another way of changing the name is to put a TStaticText view over the previous window's name. If the new name is longer than the old name, then this is trivial; if the new name is shorter, then spaces and/or horizontal border characters will need to be added for consistency. c. Likewise, the Turbo Vision source code could be modified to facilitate this feature. TWindow::title would need to be changed from a const char* to a char*. If this is done, a member function (eg. TWindow::setTitle()) would need to be added to serve as an interface to this added feature. PARADOX ENGINE 1. I am getting the error message "undefined symbol: ostream::outstr(const char far*, const char far*);" Why is this? Paradox Engine applications can only be created in the large memory model. This message may be generated if compilation is done in the small memory model. 2. I am getting an "Error 8: Primary index is out of date" message. Why? This error message can occur if the sort order of the Engine application is different from the sort order of the table. A common cause of this is using the International sort order with Paradox while using the Engine with its de- fault ASCII sort order. The sort order can be changed by using the PXSetDefaults() function. PASCAL 1. How do I hook into the timer interrupt? program DoTime; { PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/11 TITLE : C++ Language News -- Volume 1 Number 6 DSEG_Vec and Kbd_Vec are not being used here as real procedures, but as a place to store bytes in the code segment. If chaining is desired, call ScreenChain, else call ScreenRet. } uses Dos, Crt; var Ch: Char; MyPtr: Pointer; n, w: Word; procedure DSEG_Vec; assembler; asm DB 0 end; procedure Kbd_Vec; assembler; asm DB 0, 0, 0 end; procedure MyThing; assembler; asm push ds mov ds, word ptr cs:[DSEG_Vec] { get program's data } { segment } inc N { don't use WriteLn } { inside an ISR } pop DS { instead, set a flag } { or inc a value } jmp DWORD PTR CS:[Kbd_Vec] { Be sure to chain on } { to old interrupt } end; begin ClrScr; n := 0; w := dseg; move(w, pointer(@DSEG_vec^) , 2); GetIntVec($1C, Pointer(@Kbd_Vec^)); PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/11 TITLE : C++ Language News -- Volume 1 Number 6 SetIntVec($1C, @MyThing); WriteLn('Press any key to end this program'); WriteLn('Value N now equals: '); Repeat GotoXY(21, 2); { don't try anything } { this complicated } WriteLn(n); { inside a timer } { interrupt } Until KeyPressed; SetIntVec($1C, pointer(@kbd_Vec^)); end. QUIZ The ambiguity in last week's code stems from the name hiding that occurs between the two base classes (see section 13.1 of the ARM for preliminary information). These member function calls can be resolved by explicitly qualifying the base class desired; function main() would thus look like the following -- main() { foo f; bar b; foobar fb; fb.foo::member(f); fb.bar::member(b); return 0; } This week's quiz involves the code below which will output the value of zero (0) when it should print something else. Why? #include class base { int v; public: base(int i = 0) { v = i; } void print() { cout << "base::v = " << v << endl; } }; class left : virtual public base { public: PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/11 TITLE : C++ Language News -- Volume 1 Number 6 left(int i) : base(i) { } }; class right : virtual public base { public: right(int i) : base(i) { } }; class derived : virtual public left, virtual public right { public: derived(int i) : left(1), right(2) { } }; main() { derived *p = new derived(3); p->print(); delete p; return 0; } NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1371 How to create a single help view that includes all help shipping with Borland C++ for OS/2. 1373 Necessary changes required to run Bor- land C++ 3.1 for DOS under an OS/2 DOS box. 1374 Example of an manipulator which will format the output of a pointer. This works under both 16-bit DOS along with 32-bit OS/2. 1375 Explanation of password usage with the Paradox Engine. 1376 Using the newer BWCC.DLL with Resource Workshop causes some text to appear white instead of grey; this document explains how to fix this problem. 1379 How to get user input when in graphics mode. 1381 Deriving a new class from class Object when using the object-based class PRODUCT : Borland C++ NUMBER : 8505 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/11 TITLE : C++ Language News -- Volume 1 Number 6 library. 1382 Q&A concerning common BGI errors in 8514 mode. These documents can be found on either LIB-2 of BCPPDOS and BCPPWIN under 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.