PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 1/15 TITLE : Borland Language Support News - Volume 1 Number 17 _Borland Languages Support News_ ----------------------------------------------------------------- volume 1, number 17 October 4, 1993 ----------------------------------------------------------------- PASSING MEMBER FUNCTION POINTERS TO FUNCTIONS Functions and member functions are not equivalent and can- not be converted to each other. Static member functions, though encapsulated within class scope, are functionally identical to functions. The primary difference between mem- ber functions and static member functions is that member functions have access to the 'this' pointer while static member functions do not (see sections 9.3.1 and 9.4 of the ARM). Any call to a member function has the 'this' pointer pushed implicitly onto the stack as the first parameter. This article will look at what is required to pass member function pointers to functions -- including static member function pointers. Discussion will begin with the test case class definition. Here, a class 'foo' will be defined as: class foo { int v; public: foo(const int i) : v(i) { } void f() { cout << "foo::f(" << v << ")" << endl; } static void g() { cout << "foo::g()" << endl; } }; where f() is defined as a member function and g() is defined as a static member function. Two functions will be declared, h() and i() which respect- ively take a function pointer and a member function pointer as function arguments. ie. void h(void (*)()); void i(void (foo::*)()); i() taking a member function pointer as its argument is syn- tactically correct, but semantically useless if the method PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 2/15 TITLE : Borland Language Support News - Volume 1 Number 17 is to be executed. Once again, the 'this' pointer is re- quired. Thus, i() will be modified to the following -- void i(foo*, void (foo::*)()); Now the various combinations can be checked for syntactic correctness. Function h() requires a function pointer of a function taking no arguments and returning nothing. A function with this signature could easily be passed as an argument. Likewise, a static member function pointer could be passed as well. A member function pointer cannot be passed for two reasons: first, it is not syntactically equivalent; secondly, the 'this' pointer is not expected to reside on the stack. As for function i(), only member function f() can be passed because it is the only member function that will be implicitly (and expectedly) pushing the 'this' pointer onto the stack. The following example program demonstrates what can be correctly done in terms of the language specification. #include class foo { int v; public: foo(const int i) : v(i) { } void f() { cout << "foo::f(" << v << ")" << endl; } static void g() { cout << "foo::g()" << endl; } }; void h(void (*)()); void i(foo*, void (foo::*)()); void j(); int main() { foo f(0); h(j); h(&foo::g); i(&f, &foo::f); return 0; } void h(void (*fp)()) { (*fp)(); PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 3/15 TITLE : Borland Language Support News - Volume 1 Number 17 } void i(foo *p, void (foo::*mfp)()) { (p->*mfp)(); } void j() { cout << "j()" << endl; } HOT ISSUES I recently increased the amount of extended memory on my machine to a value greater than 16M and TD386 no longer works. Why? Download the file TDG16M.ZIP from either Borland's download BBS at (408)431-5096 or from LIB-2 of the BCPPDOS or BCPPWIN forums on CompuServe. This archive contains a driver which will allow TD386 to be run on machines with more than 16M of RAM installed. It does this by hooking interrupt 0x15 function 0x88 (get extended memory size) and returns a num- ber that is less than or equal to 16M. Because of the in- terrupt hooking, this device driver MUST be listed as the first line in CONFIG.SYS. DOS How can I determine what strings are embedded in an exe- cutable (binary file)? The following program searchs a file (text or binary) for 'n' contiguous printable ASCII characters. 'n' is the length of the string specified by the user on the command- line. #include #include #include #include #include PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 4/15 TITLE : Borland Language Support News - Volume 1 Number 17 #include #ifdef __cpluscplus const int DEFSTR = 5; const int MAXSTR = 10000; #else #define DEFSTR 5 #define MAXSTR 10000 #endif int main(int argc, char **argv) { FILE *fp; char c, *s, *r; unsigned long flen; int slen = DEFSTR; if (argc < 2) { printf("Usage: %s [string length]\n", argv[0]); return 1; } if (argc == 3) { /* slen parameter */ slen = atoi(argv[2]); if (!slen) { printf("===============================\n"); printf("%s:\n", argv[0]); printf("default string length = %d.\n", DEFSTR); printf("===============================\n"); slen = DEFSTR; } } fp = fopen(argv[1], "rb"); if (!fp) { printf("Couldn't open %s\n", argv[1]); return 2; } flen = filelength(fp->fd);/* get filelength */ s = r = (char*) malloc(sizeof (*r) * (MAXSTR + 1)); if (!s) { printf("insufficient heapspace available\n"); return 3; PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 5/15 TITLE : Borland Language Support News - Volume 1 Number 17 } while (flen--) { /* process entire file */ c = fgetc(fp); if isprint(c) { if ((s - r) <= (MAXSTR)) *s++ = c; else { *s = 0; /* add terminating zero */ printf("%s\n", r); s = r; } } else { /* 1st unprintable char */ if ((s - r) >= slen) { *s = 0; printf("%s\n", r); } s = r; } } fclose(fp); return 0; } OS/2 1. When writing DLL's with Borland C++ for OS/2 which are to be used with executables created with another compiler, be sure to set the '_multidll' global variable to zero. eg. extern int _multidll = 0; Only one instance of the run-time library is needed with applications created with the compiler, such that setting '_multidll' to zero instructs the DLL to lead the run-time library itself. 2. I am getting a "Linker Fatal: THREAD fixup found in module '[filename]' in module '[library name]' error message. What can I do to fix this? A fixup is a record in the object file that tells the linker PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 6/15 TITLE : Borland Language Support News - Volume 1 Number 17 that certain information (usually regarding correct address of a symbol defined in another module) is needed. The link- er uses the fixup record to locate and patch in the address for the correct symbol at link time. A thread fixup is a fixup record that references a previous thread record sim- ilar to a fixup record) to determine the frame or target of the fixup, rather than explicitly declaring the information. Borland C++ for OS/2 does not generate thread based fixup records, and this message will only be generated if third- party DLL's, etc. are being linked together. In this case, the best solution is to recompile all code with Borland C++ for OS/2. WINDOWS What is the EasyWin library? To understand what EasyWin is, it first helps to know the differences between "true" Windows programming, and tra- ditional sequential (typical of the DOS environment) pro- gramming. In traditional programming, the functions printf() & scanf() are heavily used to display and retrieve information to and from the user. Here, "traditional" means DOS-style programs that are not specifically designed for running under Windows. Traditional programs generally have direct access to the screen and keyboard, also known as stdout (standard out) and stdin (standard in). In the Windows environment, however, a program does not have such direct access to these devices to communicate with a user. Without the EasyWin library, func- tions like printf() have no meaning to Windows. One would have to write many lines of code to replace the functional- ity of printf(). Another difference is the use of the functions main() versus WinMain(). Traditional programs must have a special function named main(), which will be the first function to execute. In Windows programming, this special function must be named WinMain(). The primary reason this difference is to avoid confusion, since main() takes a different set of arguments than WinMain(). It also helps in distinguishing whether PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 7/15 TITLE : Borland Language Support News - Volume 1 Number 17 the code is for a Windows program or not. EasyWin helps bridge this gap between these two environments. It allows the programmer to use functions like printf() and scanf(). It provides a generic "text" window, where output from printf() is displayed. It also allows you to write Windows programs without specifically defining a WinMain() function. So the EasyWin library allows many DOS-style pro- grams to be compiled and executed in the Windows environment without changing a line of code. One other example of the differences between a traditional program and a true Windows program is flow of execution. In traditional programs, the programmer determines what and when procedures are executed. On the other hand, a true Windows program is known as "event-driven," where the Windows en- vironment will determine what and when your procedures are executed, based on what the user does. By using the EasyWin library, however, the non-event-driven program can be made to run under Windows, but it does not address the needed rede- sign in code and logic of a program to convert it from a traditional application to a true Windows application. TURBO VISION A button created with the bfDefault flag will be the default button in a dialog box if a view other than another button has focus. When a dialog box is initially created, the last view inserted will have focus. This may or may not be a button created within the bfDefault flag. This behaviour is by design as specified in the _Common User Access_ guide published by IBM. This is illustrated in the example TVGUID16.CPP. The "OK" button was created with the bfDefault flag. When the dialog box is initially brought up, the "Cancel" button has focus. This is because the "Cancel" button was inserted last into the dialog box. If the "Consistency" (radio choice), "Cheeses" (check box), or "Delivery Instructions" (input line) views have focus in the dialog box, the "OK" button will be the default ("k" will be in blue). PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 8/15 TITLE : Borland Language Support News - Volume 1 Number 17 The view to have focus when a dialog box is initially created can be controlled by using the member function selectNext() from class TDialog, or using select() from TView. A parameter of False to selectNext() will cause the next view in the linked list to be chosen or a parameter of True will cause the previous view to be chosen. PARADOX ENGINE I run my Paradox Engine 3.0 application, and the call to the constructors do not work. In particular, if I call the BEngine constructor, it fails and lastError is set to PXERR_INVALIDTYPE (400). Yet, I know I am passing a valid constant to the constructor. What is happening? The database library for version 3.0 of the Engine is built with "treat enums as ints" (Options|Compiler|Code Gener- ation). This means that the BEngine constructor is ex- pecting a full two-byte value on the stack. If the ap- plication under development is built without this switch on, the compiler will assume that the size of the BEngineType is a byte, and it will place onto the stack a value that the library's constructor will not read correctly. Effectively, the compiler will -- mov al, 1 // if pxLocal is used push ax which leaves garbage in the high order byte. This would be OK if the library was expecting a byte, but since it is ex- pecting an entire word, the high order byte will contain an incorect value. PASCAL The following program demonstrates how to detect if a con- trol in a Turbo Vision dialog has received the focus. The key is the cmReceivedFocus message, which gets sent to the owner of a control whenever that control is about to re- ceive the focus. Here is sample code showing how to re- spond to the message: PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 9/15 TITLE : Borland Language Support News - Volume 1 Number 17 procedure TTrainDialog.HandleEvent(var Event: TEvent); begin if (Event.What = EvBroadCast) and (Event.InfoPtr = MyInputLine) and (Event.Command = cmReceivedFocus) then begin MessageBox('Got cmReceivedFocus Message', nil, mfOkCancel); end; TDialog.HandleEvent(Event); end; The above code sample assumes that an inputline has been inserted into a dialog. Whenever the user tabs to the in- put line, then the cmReceivedFocus message is sent to the control's owner. Note that its not sent to the control itself, but to the control's owner! Note also that this is a broadcast message. Of course, it is not enough to just pick up on broadcast cmReceivedFocus messages. You will also have to detect which control has just received the focus. To do this, you can use the InfoPtr field of the incoming TEVENT record. This field will contain a pointer to the control that is about to receive the focus. The example code here literally compares the pointer to a second copy of a pointer to that control. If they are e- qual, then you know that you have the right control. Of course, the above code assumes that you have explicitly saved and declared a pointer to the inputline, as shown in the included example program. Now that you understand how cmReceivedFocus messages work, you might be interested in seeing another way of handling the same problem. This is to override the SetState function which actually generates cmReceivedFocus messages. In the program shown below, the following code fragment will enable and disable the state of a command on the statusline, de- pending on whether or not the control has the focus. procedure TMyButton.SetState(AState: Word; Enable: Boolean); begin inherited SetState(AState, Enable); if (AState = sfFocused) and (Enable = False) then PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 10/15 TITLE : Borland Language Support News - Volume 1 Number 17 DisableCommands([cmFoo]); if (AState = sfFocused) and (Enable = True) then EnableCommands([cmFoo]); end; The key to the above code is that it checks for sfFocused messages to find out whether the control is receiving or losing the focus. When running the following program, watch the status line to see which control has the focus. program DlgFocus; uses App, Dialogs, Drivers, Menus, MsgBox, Validate, Views, Objects; const cmDialogBox = 101; cmTest = 102; cmButton = 103; cmInput = 104; InputLength = 128; TheMessage = 'The Input line just received focus.' + #13#10 + 'Chose OK to set Ok in the Inputline' + #13#10 + 'Chose Cancel to set Cancel in the ' + 'InputLine'; type PMyButton = ^TMyButton; TMyButton = Object(TButton) procedure SetState(AState: Word; Enable: Boolean); virtual; end; PTrainDialog = ^TTrainDialog; TTrainDialog = Object(TDialog) MyInputLine: PInputLine; constructor Init(Bounds: TRect; ATitle: String); procedure HandleEvent(var Event: TEvent); virtual; end; TMyApp = Object(TApplication) PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 11/15 TITLE : Borland Language Support News - Volume 1 Number 17 constructor Init; procedure InitStatusLine; virtual; procedure HandleEvent(var Event: TEvent); virtual; procedure DialogBox; end; procedure TMyButton.SetState(AState: Word; Enable: Boolean); begin inherited SetState(AState, Enable); if (AState = sfFocused) and (Enable = False) then DisableCommands([cmButton]); if (AState = sfFocused) and (Enable = True) then EnableCommands([cmButton]); end; constructor TTrainDialog.Init(Bounds: TRect; ATitle: String); var R: TRect; Control: PView; S: String; begin TDialog.Init(Bounds, ATitle); R.Assign(2, 2, 37, 4); Insert(New(PMyButton, Init(R, 'Test', cmTest, bfNormal))); R.Assign(3, 5, 37, 6); MyInputLine := New(PInputLine, Init(R, InputLength)); Insert(MyInputLine); R.Assign(2, 4, 24, 5); Insert(New(PLabel, Init(R, 'Delivery instructions', MyInputLine))); R.Assign(2, 7, 37, 9); Insert(New(PButton, Init(R, 'O~k~', cmOk, bfDefault))); S := '32'; SetData(S); end; procedure TTrainDialog.HandleEvent(var Event: TEvent); var Result: Word; PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 12/15 TITLE : Borland Language Support News - Volume 1 Number 17 S: String; begin if (Event.What = EvBroadCast) and (Event.InfoPtr = MyInputLine) then case Event.Command of cmReceivedFocus: EnableCommands([cmInput]); cmReleasedFocus: DisableCommands([cmInput]); end; TDialog.HandleEvent(Event); end; constructor TMyApp.Init; begin inherited Init; DisableCommands([cmInput,cmButton]); end; procedure TMyApp.HandleEvent(var Event: TEvent); begin TApplication.HandleEvent(Event); if Event.What = EvCommand then begin case Event.Command of cmDialogBox: DialogBox; else Exit; end; ClearEvent(Event); end; end; procedure TMyApp.InitStatusLine; var R: TRect; begin GetExtent(R); R.A.Y := R.B.Y - 1; StatusLine := New(PStatusLine, Init(R, NewStatusDef(0, $FFFF, NewStatusKey('', kbF10, cmMenu, NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit, NewStatusKey('~Alt-D~ Dialog', kbAltD, cmDialogBox, NewStatusKey('~Alt-F4~ Button Focused?', kbAltF4, cmButton, PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 13/15 TITLE : Borland Language Support News - Volume 1 Number 17 NewStatusKey('~Alt-F5~ InputLine Focused?', kbAltF5, cmInput, nil))))), nil) )); end; procedure TMyApp.DialogBox; var R: TRect; D: PDialog; begin R.Assign(20,5,60,15); D := New(PTrainDialog, Init(R, 'Hit Tab, watch statusline')); if ValidView(D) <> Nil then DeskTop^.ExecView(D); Dispose(D, Done); end; var A: TMyApp; begin A.Init; A.Run; A.Done; end. QUIZ Last week's quiz/exercise focused again on operator over- loading where substrings were to be obtained with the following notation: string s = "Hello, world!"; // ... string t = s(j, k); With this example, 'j' is the starting index of the sub- string to be copied. 'k' is the number of characters in the substring. One possible solution which does no error checking is -- #include PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 14/15 TITLE : Borland Language Support News - Volume 1 Number 17 #include #include class string { char *s; public: string() { s = 0; } string(const char*); string operator()(const int, const int); operator const char*() { return s; } ~string() { if (s) delete [] s; } }; string::string(const char *p) { s = new char[strlen(p) + 1]; assert(s != 0); strcpy(s, p); } string string::operator()(const int i, const int j) { const int n = 80; char a[n] = { 0 }; return strncpy(a, s + i, j); } int main() { string s = "Hello, world!"; string t = s(7, 5); cout << "s = " << s << endl; cout << "t = " << t << endl; return 0; } For this week's quiz/exercise, write a function which will output the binary representation of a floating point value. NEW TECHNICAL INFORMATION DOCUMENTS AVAILABLE Some of the latest technical information documents available to customers include the following. 1560 Function similar to the DOS spawn() function for spawning a child process from within Windows. PRODUCT : Borland Languages NUMBER : 8516 VERSION : All OS : All DATE : October 25, 1993 PAGE : 15/15 TITLE : Borland Language Support News - Volume 1 Number 17 1559 Explanation of how to create a pointer to real mode memory (BIOS in this case) while in protected mode using Borland Pascal. 1378 List of various vendors who supply SVGA BGI drivers. 1004 Information on how to "pack" a Paradox Engine database file, or table. 733 How to use variable arguments. 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.