?? chapter3.html
字號:
<b>Porting aids for Microsoft C8 segmented architecture</b>#define FAR _far#define NEAR _near#define FASTCALL _fastcall#define PASCAL _pascal#define EXPORT _export#define BASEDIN(seg) _based(_segname(#seg))<br><b>Porting aids for flat model programs</b>#define FAR#define NEAR#define FASTCALL#define PASCAL#define EXPORT#define BASEDIN(seg)</pre></tr></td></table> <br>FAR and NEAR. These are used to abstract out the near- and far-segmented architecture. NEAR implies a 16-bit pointer and FAR implies a 32-bit pointer. When porting to a non-segmented architecture, these can be defined to be nothing. <br><br>FASTCALL and PASCAL. These are used to specify the calling convention of a function primarily for optimization purposes. When porting to a non-segmented architecture, these can be defined to be nothing. <br><br>EXPORT. This define is applicable to Windows DLL programming. Otherwise, it can be defined to be nothing. <br><br>BASEDIN. This define is primarily used by the CSCHAR macro to place character strings within a code segment primarily for optimization purposes. When porting to a non-segmented architecture, it can be defined to be nothing. <br><br>In most cases, these macros are used in other macros or in typedef's so that the code base is not cluttered up. For example, to declare a far pointer to a char so that it works equally well under a segmented and non-segmented architecture, you could do the following. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Using a far pointer to a char, not a good idea</b>char FAR*lpMem;</pre></tr></td></table> <br>However, using char FAR* will just clutter up all the source files with FAR. The solution is to use a typedef to declare what a far pointer to a char is once. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>LPSTR typedef, a better idea</b>typedef char FAR*LPSTR;</pre></tr></td></table> <br>Now LPSTR should be used instead of char FAR*. The concept of trying to hide how something works provides an abstraction that aids porting and allows for clean source code. <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td> Try to hide how something works to provide an abstraction that aids </tr></td></table></blockquote> <br><br><b>3.2.2 Using EXTERNC</b> <br><br>If you are coding under C++, name mangling can sometimes be a problem. This happens under Windows DLL coding when a .def file is used. Functions that are exported must be specified in the .def file, but name mangling can make it almost impossible to type in the names manually. Luckily, C++ provides a solution in the form of a linkage specification. The #define's you can use are as follows. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>EXTERNC macro</b>/*--- EXTERNC depends upon C/C++ ---*/#ifdef __cplusplus#define EXTERNC extern "C"#else#define EXTERNC#endif</pre></tr></td></table> <br>Under C++, EXTERNC gets defined to be extern "C". Under C, EXTERNC gets defined to be nothing. EXTERNC is used in function prototypes as follows. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Using EXTERNC</b>EXTERNC type APIENTRY FunctionName( argument-types );</pre></tr></td></table> <br>You need to use EXTERNC only in the prototype for a function, not in the source code where the function is actually written. This is how Microsoft C8 works.<br><a name="winassert"><br></a><big><b>3.3 Using WinAssert() Statements</b></big> <br><br>The WinAssert() statement is the classic assertion statement with a few twists. Why use assertion statements? The key reason is to verify that decisions and assumptions made at design-time are working correctly at run-time. There is a difference between WinAssert() and <a href="chapter2.html#compilerassert">CompilerAssert() §2.1.4</a>. Both check design-time assumptions, but CompilerAssert() provides the check at compile-time, whereas WinAssert() provides the check at run-time. <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td> Assertion statements provide run-time checking of design-time assumptions. </tr></td></table></blockquote><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Microsoft C8 assert() macro, do not use</b>#define assert(exp) \ ( (exp) ? (void) 0 : _assert(#exp, __FILE__, __LINE__) )</pre></tr></td></table> <br>An assert macro is provided by the Microsoft C8 library in assert.h. It takes any boolean expression. Nothing happens when the assert macro is true. If the assert macro is false, however, the _assert() function is called with a string pointer to the text of the boolean expression, a string pointer to the text of the source file and an integer line number where the error occurred. Usage of the stringizing operator (#) is described in <a href="chapter2.html#stringizing">§2.2.7</a> . This information is then formatted and displayed by _assert(). <br><br>A problem with this macro is that it ends up placing too many strings in the default data segment. One easy solution is to remove the #exp argument, which is turning the boolean expression into text. After all, the file and line number are all that are needed to look up the boolean expression. Also, every time the assert() macro is used, a new string __FILE__ is created. Some compilers are able to optimize these multiple references into one reference, but why not just fix the problem? My solution to the problem is to declare a short stub function at the top of each source file which references __FILE__. WinAssert() then calls this stub function with the current line number. <br><br><table bgcolor="#F0F0F0"><tr><td><img src="./windows.gif">There is an additional problem that is specific to the Windows programming environment. In Windows DLLs, it is possible to declare a function that, when called, does not switch to the DLLs data segment, but instead keeps the current caller's data segment. If you were to use an assertion statement in one of these functions, the string pointer to the filename would be incorrect. The solution is to declare the __FILE__ string to be a code segment (<a href="chapter2.html#cschar">CSCHAR §2.1.8</a>) variable. This way, it does not matter what data segment is current. </td></tr></table> <br>An interesting twist that has been added to WinAssert() is that it supports writing code that is fault-tolerant. If a design-time assumption has failed, should you really be executing a section of code? I say no! The WinAssert() statement may be followed by a semicolon or by a block of code. The block of code will be executed only if the assertion succeeds. <blockquote><table bgcolor="#E0E0E0" border=1 cellpadding=2 cellspacing=0><tr><td> Use WinAssert() on a block of code to produce code that is fault-tolerant. </tr></td></table></blockquote><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>WinAssert(), non-fault-tolerant syntax</b>WinAssert(expression);<br><b>WinAssert(), fault-tolerant syntax</b>WinAssert(expression) { (block of code) }</pre></tr></td></table><br>The WinAssert() is implemented through a set of macros as follows.<br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>WinAssert() implementation</b>#define USEWINASSERT CSCHAR szSRCFILE[]=__FILE__; \ BOOL static NEAR _DoWinAssert( int nLine ) { \ ReportWinAssert(szSRCFILE, nLine); \ WinAssert(nLine); \ return(FALSE); \ }#define AssertError _DoWinAssert(__LINE__)#define WinAssert(exp) if (!(exp)) {AssertError;} else</pre></tr></td></table> <br>If WinAssert() is used in a source file, USEWINASSERT must appear at the top of the source file somewhere. <br><br>In addition to the WinAssert() macro, an AssertError() macro is provided for those times that you want to unconditionally force an error to be reported. <br><br>The reporting process of an assertion failure starts by calling a function that is local (private) to the source file. The function is _DoWinAssert() and the argument is the line number where the failure occurred. The body of _DoWinAssert() is straightforward except for the inclusion of WinAssert(nLine). Since the line number is never zero, this appears to have no purpose. This trick forces _DoWinAssert() to be compiled into the module, even if there are no references to the function in the rest of the file. Otherwise, Microsoft C8 removes the unreferenced function. <br><br>Another subtle problem is that if _DoWinAssert() is declared to be a LOCAL function (described in <a href="chapter6.html">Chapter 6</a>), the optimizing Microsoft C8 compiler will not build a stack frame for this function. For this reason, it has the static NEAR attributes instead of the LOCAL attribute, which allows the stack frame to be built. <br><br>In addition to these defines, WinAssert() requires that ReportWinAssert() be defined somewhere. I define it in a DLL so that the function needs to be coded only once. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>ReportWinAssert() function prototype</b>EXTERNC void APIENTRY ReportWinAssert( LPSTR, int );</pre></tr></td></table> <br>Once done, any other application has access to it. ReportWinAssert() allows you to display the assertion error in whatever way is appropriate at your organization. In my ReportWinAssert(), I log the filename, line number and stack trace to a log file and issue a system modal message box requesting that the user report the error. See <a href="appendix.html#reportwinassert">§A.7</a> for example implementations. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>Using WinAssert()</b>#include <app.h>USEWINASSERT . . .void LOCAL TestingWinAssert( int nValue ){ WinAssert(nValue>0) { ... }} /* TestingWinAssert */</pre></tr></td></table> <br>One of the key things you must remember is that the argument to WinAssert() must have absolutely no side effect on any variables. It must only reference variables. <br><br><table bgcolor="#CCCCEE" cellpadding=0 cellspacing=0><tr><td><pre><b>WinAssert(), used incorrectly</b>WinAssert((x/=2)>0);<br><b>WinAssert(), used correctly</b>x /= 2;WinAssert(x>0);</pre></tr></td></table> <br>This is in case a policy of removing asserting statements from the code before releasing the product is enforced. While I do not
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -