107
C++ Programming Guidelines John M. Dlugosz 26-August-2001 Copyright © 2001 by John M. Dlugosz http://www.dlugosz.com This publication may be used freely, with attribution, for all purposes including distributing printed and electronic copies, provided no fee is charged for such distribution, this copyright notice and information remains intact, and any changes to the content are clearly marked as non-original material. Printed copies should be made with the correct fonts, as indicated below. The content in this document may be used as a base for derivative works, provided this document is cited. This document uses the following fonts, all of which come with Windows, Microsoft Word ’97, the associated “value pack”, and may be available on Microsoft's web site: Bookman Old Style Comic Sans MS Arial Arial Black Arial Rounded MT Bold Lucida Sans Lucida Sans Typewriter Wingdings Wingdings 2 Franklin Gothic Demi Cond

Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

C++ Programming GuidelinesJohn M. Dlugosz26-August-2001

Copyright © 2001 by John M. Dlugoszhttp://www.dlugosz.comThis publication may be used freely, with attribution, for all purposes including distributing printed and electronic copies, provided no fee is charged for such distribution, this copyright notice and information remains intact, and any changes to the content are clearly marked as non-original material. Printed copies should be made with the correct fonts, as indicated below.The content in this document may be used as a base for derivative works, provided this document is cited.This document uses the following fonts, all of which come with Windows, Microsoft Word ’97, the associated “value pack”, and may be available on Microsoft's web site:

Bookman Old StyleComic Sans MSArialArial BlackArial Rounded MT BoldLucida SansLucida Sans TypewriterWingdingsWingdings 2Franklin Gothic Demi CondSymbol

Page 2: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

ContentsIntroduction......................................................................................................6

Considerations..............................................................................................6Scope............................................................................................................7

C++ Header Files.............................................................................................8Standard Structure of a .H file.......................................................................8Header Content Restrictions.......................................................................10Format of the #include statement..............................................................11Minimize Compilation Dependencies Between Files....................................12

Names............................................................................................................13Globally Unique Identifier Names................................................................13DLL Names..................................................................................................14Usable #define Names................................................................................15Reserved Names.........................................................................................15

Comments......................................................................................................17Be Useful.....................................................................................................17Formatting Of Comments............................................................................18No Verbose Comments................................................................................19Don’t Mix Code With Documentation..........................................................20Revision Control Information.......................................................................22C style vs. C++ Style..................................................................................24Commenting Out Blocks of Code................................................................24

Namespaces...................................................................................................25DLLs...............................................................................................................26

Embed Version Information in DLL..............................................................26Exporting Symbols......................................................................................30DLL-specific Resources................................................................................34Initialization and Shutdown Issues..............................................................34Linking........................................................................................................37

Compiler Options............................................................................................38Standard Build Options...............................................................................38Options for Efficient Code...........................................................................40Options for Programmer Convenience........................................................41Run-Time Library in a DLL or Statically Linked?...........................................41

Error Handling................................................................................................42Write Pro-EH Code.......................................................................................42Exception Specifications.............................................................................42Functions Return Results.............................................................................42Constructors and Destructors.....................................................................42Catch References........................................................................................42Don’t Throw Pointers...................................................................................42Use Standard Exception Types....................................................................42

Internationalization........................................................................................43Don’t Assume the Whole World is Latin-1...................................................43Watch Out For Multibyte Characters...........................................................43Distinguish OEM and ANSI Character Set....................................................44

C++ Language Usage....................................................................................46Casting........................................................................................................46Error Checking is Good................................................................................46Avoid Macros...............................................................................................46

C++ Programming Guidelines Page 2John M. Dlugosz 18-July-1999

Page 3: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Use Smart Pointers......................................................................................48Use Value Semantics...................................................................................48Promote Strong Type Checking...................................................................48Use RTTI, Not Home-Brew...........................................................................48

Memory Management....................................................................................49Match frees with allocations........................................................................49Exceptions vs. NULL for ‘new’.....................................................................50

C++ Declarations...........................................................................................53Avoid Global Constructors and Destructors.................................................53Declaring Const Objects..............................................................................56Choosing Names.........................................................................................57Boolean variables........................................................................................57Enumeration Types......................................................................................57Use Readable Enums Rather Than Bool......................................................57Use Anonymous Namespaces for File-Local Things.....................................58Pointers vs. References...............................................................................58Formatting Pointer/Reference Declarations.................................................58Declare (and initialize) Variables Where Needed........................................58Avoid “Out” Parameters..............................................................................59Don’t Use (void) Parameter List..................................................................59

Overloading....................................................................................................60When functions do the same thing but operate on different types they should have the same name.......................................................................60

C++ Statements............................................................................................61Switch Statements......................................................................................61Return Statements......................................................................................61Goto Statements.........................................................................................61

Classes...........................................................................................................62Canonical Object Form................................................................................62General Constructor Issues.........................................................................63General Destructor Issues...........................................................................63Default Constructor Issues..........................................................................65Copy Constructor Issues.............................................................................65prefix and postfix ++ and −−.....................................................................65Assignment Operator Issues.......................................................................65“other” copy functions................................................................................66Use ‘explicit’ Constructors..........................................................................67Use Objects To Wrap Resources..................................................................68

C++ Libraries.................................................................................................69Old vs. New Standard Headers....................................................................69Use new/delete for Memory Management...................................................69STL is Like Pointers......................................................................................69

Memory Management....................................................................................70Encapsulate Non-Standard Memory Management......................................70Use Proper Vector/Scalar Form of Delete....................................................70Class-Specific New/Delete...........................................................................70Placement Arguments (Overloading New)..................................................70Errors in Operator New................................................................................70

Threading Issues............................................................................................71Thread-Specific Data...................................................................................71

C++ Programming Guidelines Page 3John M. Dlugosz 18-July-1999

Page 4: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Mutual Exclusion.........................................................................................71Static Local Variables..................................................................................72

Disks and Files................................................................................................73Disk and File Size........................................................................................73File Name Delimiters...................................................................................73File Names..................................................................................................74

Templates.......................................................................................................75Put Common Code Into a Non-Template Base Class....................................75

Miscellaneous Conventions............................................................................76Dates..........................................................................................................76Cause and Effect Should Be Close Together................................................76Expressiveness............................................................................................76

Windows 95 vs. NT.........................................................................................78ANSI or Unicode Version of API....................................................................78Functions Available only in NT or 95...........................................................78

Win32 Guidelines...........................................................................................80File Installation Locations............................................................................80Managing Common Files.............................................................................80Consistent use of TCHAR.............................................................................82

Win32 Faux Pas’s............................................................................................84Tray Icons Should Watch for Restarts..........................................................84

Appendix A: Reprinted Papers........................................................................85Two-stage Destructors.................................................................................86

Appendix B: Bibliography...............................................................................89Appendix C: References.................................................................................90Appendix D: Listings.......................................................................................91

CheckOEM...................................................................................................91Index..............................................................................................................92

C++ Programming Guidelines Page 4John M. Dlugosz 18-July-1999

Page 5: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Rule ConcordanceRule 1. Use the standard structure for a .H file.............................................10Rule 2. Use UUID format (preferred) or standard “user friendly” format for

global names.....................................................................................13Rule 3. Don't use names that begin with an underscore or contain a double-

underscore........................................................................................16Rule 4. No useless comments.......................................................................18Rule 5. Don’t put huge lists of formal documentation comments on every

declaration........................................................................................21Rule 6. Use thoughtful prose, not a laundry list of attributes to document

something.........................................................................................22Rule 7. Include project and version identification information in all shipped

CPP and H files..................................................................................24Rule 8. Use #if 0 to “comment out” blocks of code.......................................24Rule 9. Always use the correct instance handle when writing code for a DLL.

34Rule 10.Consider users that have different character sets.............................43Rule 11.Don’t use casts where none are needed...........................................46Rule 12.Use dynamic_cast when downcasting...............................................46Rule 13.Avoid preprocessor macros in favor of other language mechanisms.48Rule 14.Avoid static constructors and destructors.........................................56Rule 15.Qualify global const variables with static or extern...........................57Rule 16.Use the real bool type, not a substitute............................................57Rule 17.Classes must contain the 4 canonical members...............................63Rule 18.Constructors should be robust..........................................................63Rule 19.Destructors should generally be virtual............................................63Rule 20.Exceptions may not escape from destructors...................................65Rule 21.A destructor may be called at any time............................................65Rule 22.Extra constructors don’t remove the requirement for a copy

constructor to be defined..................................................................67

C++ Programming Guidelines Page 5John M. Dlugosz 18-July-1999

Page 6: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

IntroductionConsiderationsThe fundamental goals of this design is to produce code that is robust and maintainable.Notice that readable is not listed as a primary goal. Instead, readability is merely one aspect of maintainability. But maintainability covers broader and deeper ground.Writing the most efficient code is not part of this document. Often the issues of being robust (e.g. checking array subscripts) and maintainable (e.g. isolating a subsystem) seem to contradict the goal of efficiency.In fact, such things have a linear impact on the program’s performance. By linear impact, I mean it may slow down by 20% or some such, which is a linear equation. So, don’t be penny-wise and pound-foolish. A 20% performance change is nothing in the grand scheme of things! First of all, CPU speed is doubling every few years, so you can generally assume that the specified hardware will be fast enough for the desired software. Second, linear changes are not where real performance gains come from.Algorithms make or break the program’s performance. Writing a O(n2) function where you could have used O(n log n) is a big difference. We’re talking orders of magnitude here, not a percentage or even a small multiplier. Writing maintainable code means that algorithms can be improved later, for bigger gain than writing slightly faster but inflexible code originally.Naturally, some special routines may need to be highly optimized, but that is a small part of the general development process.Many of the issues discussed in this document assume that the primary goal of our software is to work correctly, work reliably, and readily evolve towards future needs. Additionally, some of the issues are from the point of view of internal reusable component developers, which require more guidelines than final applications.

ScopeThis tends to cover implementation architectural issues, and leans away from source code formatting. There is more universal truth to “correctness” than to “aesthetically pleasing”.One time in a code review, I found that the programmers present really didn’t care about a few formatting issues that were pointed out by QA personnel. Empirically, exact details of formatting seems to be rather unimportant. So, the exact formatting details should not be harped on by rule-strict ninnies to the same extent that a compiler complains about outright syntax errors. If the programmers can read it OK, the formatting is OK.

C++ Programming Guidelines Page 6John M. Dlugosz 18-July-1999

Page 7: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

More important is identifying issues in the implementation that lead to incorrect code, such as true programming rules that are not well known or considered; and standards for interoperability to promote code reuse. The compiler doesn’t catch them, but such violations should indeed be considered “wrong” in a formal sense.Second, maintainability issues can be things that are not “wrong”, but hurt you in the long run. Unlike the first category, such things may be deferred or ignored on a case-by-case basis.

C++ Programming Guidelines Page 7John M. Dlugosz 18-July-1999

Page 8: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

C++ Header FilesStandard Structure of a .H file

updated 8-September-2000A public header file for reusable code should have this overall structure:

// comment block

#pragma once#if !defined unique_guard_symbol#define unique_guard_symbol

#if !defined unique_export_symbol#define unique_export_symbol __declspec(dllimport)#endif

#include "classics\exception.h"

#define PUBLISH unique_export_symbol

namespace xxx {

using classics::ulong;

class whatever {…};

} // end namespace xxx

#undef PUBLISH

#endif // include only once

1. A comment block describing the file goes at the top. See Revision Control Information on page 22 for more on this block comment.

2. This pragma ensures that the header file is only included a single time. This is seen before the common #ifdef/#define mechanism for this, and is superior in terms of compilation speed. Alternatively, use the form shown in Globally Unique Identifier Names on page 13 if #pragma once is known to be a problem or is not available. Or, both mechanisms can be present in the same file, as shown in the example: if #pragma once is respected and is working properly, then the compiler goes no farther and doesn’t have to process the conditional compilation. Otherwise, the conditional compilation is used.

C++ Programming Guidelines Page 8John M. Dlugosz 18-July-1999

Page 9: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

3. This mechanism is used for header files associated with DLLs, and is not present otherwise.It allows the same header file to export symbols when compiling the DLL itself, or import symbols when using that DLL in other code. This is explained in more detail elsewhere.The name of the symbol is unique, as defined in Globally Unique Identifier Names on page 13. For sample.dll in this example, each associated header file uses the same symbol. This mechanism is explained in more detail in Exporting Symbols on page 29.

4. Include files pulled in by this include file, commonly called “nested includes”, go here. The include path should be qualified with the library name, even if in the same directory as this header. See Format of the #include statement on page 10 for more on include files.

5. The unique identifier used in step 3 prevents clashes but is difficult to use in code. So the PUBLISH symbol1 is defined for use only within the body of this header. The declarations in the header can be prefixed with PUBLISH to indicate that they are part of the DLLs public interface.It is important that the PUBLISH symbol is defined after any nested includes, and is undefined at the end of this file. That makes PUBLISH essentially local, and each header may use it for this same purpose, even though they belong to different DLLs.This cooperation breaks down if some include file uses it for some other purpose, but this can be easily dealt with if the need ever arises2.

6. The body of the header is wrapped in the namespace associated with this library. Note that everything else goes inside this namespace. It is recommended that the closing brace of the namespace be commented.

7. Any using declarations or using directives go inside the namespace. You shouldn’t have any, but that’s another story (see Header Content Restrictions on page 9).

8. The bulk of the header file contains declarations. This is the purpose of the header. Everything else is overhead which wraps this stuff.

Rule 1. Use the standard structure for a .H file.

Header Content RestrictionsA “component”, meaning a reusable piece of C++ code (usually a class) is accessed by its clients by including a header file. Ideally, the header will provide access to that component alone and not have side effects.

1 Previous versions of this document used EXPORT instead of PUBLISH. Thanks to 杨立明 for pointing out that EXPORT was in fact used by Microsoft’s MAPI header, and suggesting PUBLISH as a suitable replacement.2 A trivial way of dealing with it is to simply include the problematic header after any headers that use this mechanism.

C++ Programming Guidelines Page 9John M. Dlugosz 18-July-1999

Page 10: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Furthermore, use of that component should not affect the use of other components. Finally, you need to beware of proliferating or circular dependencies on libraries, especially when using DLLs.

Don’t include <windows.h> in a public header unless the component is tied to a particular framework.This Win32 system header is notorious for being unfriendly. Besides declaring thousands of functions and global identifiers for everything in Windows, it also contains several thousand macros that can cause problems with other code. This is especially true when <windows.h> is suddenly introduced into code that didn’t use it before—many people report that unexpected macros cause major or subtle changes to the code.Other headers may conflict with <windows.h> either by design (because it also defines the same system calls or structures) or by accident, because of the rampant global name pollution. So it’s easy for some code to be allergic to <windows.h>. Here is an example from Microsoft’s own development tools:

From Matt Pietrek, Microsoft Systems Journal Volume 12 Number 1.If everything I’ve described so far is correct, it should be relatively easy to construct a very small sample program that calls NtQueryInformationProcess and spits out the results of a query. Having been there and done that, I can tell you of some pitfalls that you’d encounter along the way. The first of these stumbling blocks is that NTDDK.H and WINDOWS.H do not get along. If you try to #include both WINDOWS.H and NTDDK.H in the same source file, you’ll get numerous compiler errors.

The reason for the compiler errors is that many of the include files in the DDK overlap with files in the Win32 SDK (and by implication, the Visual C++¨ version of those files). These overlapping files (WINNT.H, for instance) aren’t identical to their SDK counterparts. I tried for quite some time to get some combination of DDK files and SDK files to work together, but finally gave up. Instead, I extracted just enough of NTDDK.H to get the necessary definitions and created the NTQUERYINFORMATIONPROCESS.H file. The demo program that I’ll describe later on uses NTQUERYINFORMATIONPROCESS.H rather than NTDDK.H.

This report says a lot about the poor engineering of <windows.h>, but also tells us that an unrelated component would be difficult to use in a device driver if that component happened to drag in <windows.h> in its own header.

Follow a set of coupling rules when permitting nested includes. Why…?

Follow rules regarding using declarations, using directives, and global symbols. More details to follow…

Format of the #include statementComponent FilesBecause multiple libraries may be used in a single program, it is important that the public headers don’t get mixed up.

C++ Programming Guidelines Page 10John M. Dlugosz 18-July-1999

Page 11: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

The public headers of a reusable library should be distributed within a subdirectory named after the component. The user than points the compiler at the parent of that directory as a place to find files. The #include preprocessor statements then use quotes (not angles) and a path name starting from the distributed subdirectory.For example, a library named lparser contains five headers named tree.h, leaf.h, common.h, io.h, and nodes.h. These are distributed as a subdirectory named lparser containing these five files.The user then configures his compiler to look for include files in d:\work\libraries, and installs lparser as d:\work\libraries\lparser.Now, the nested includes within the lparser headers have the following form:

//this is file tree.h#pragma once#include “lparser\common.h”#include “lparser\nodes.h”...

This assures that tree.h will compile as part of your program even if there are is another library in your project that also contains a file called common.h or nodes.h. In short, all include file names, even to the same component, are qualified with the component name.Standard HeadersUse the standard name and angle brackets for ISO C and C++ standard headers, as well as extra headers provided by the compiler vendor.SpellingRespect the case of file names. Even though the Win32 platform is case insensitive3, writing <IOSTREAM> instead of <iostream> is bad style, and technically incorrect in the case of the standard headers.Under Win32, file name parts are separated with backslashes. However, some4 programs tolerate forward slashes as a synonym. It’s best to use native backslashes, but if cross-platform compatibility of a source file is an issue, forward slashes are acceptable.In general, every use of a particular #include file should be spelled in exactly the same way. Don’t use different case or different relative paths to the same file. This can confuse some analysis tools, and has even been known to confuse Microsoft5.

3 Actually, NT allows file names to differ only by case, so you could have two files, x.h and X.H, in the same directory. You have to know what you’re doing in order to achieve this or get away with it. If you’re using POSIX tools on NT, this might be a common occurrence.4 “some” as in not “all”. Also, I’ve observed programs that like either OK as long as you are consistent, but don’t like them mixed.5 Specifically, the Browser sees duplicate symbols, and #pragma once doesn’t work right.

C++ Programming Guidelines Page 11John M. Dlugosz 18-July-1999

Page 12: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Minimize Compilation Dependencies Between Files

C++ Programming Guidelines Page 12John M. Dlugosz 18-July-1999

Page 13: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

NamesGlobally Unique Identifier NamesGlobal names, particularly preprocessor symbol names, can still cause conflicts when code from multiple sources are combined.A particularly common case is the “include file interlock” mechanism. When #pragma once or equivalent (non-standard) mechanism cannot be used, the common idiom to prevent multiple inclusion is:

#if !defined XXX#define XXX// ... body of header goes here#endif

The problem is that names (here shown as XXX) are pulled out of a hat, and may not be unique. Two different libraries might both have a FOO_INCLUDED symbol, and this causes hard to find compilation problems if the two ever appear in the same translation unit.So in general, if you have a global symbol that doesn’t need to be “user friendly”, as with the include file interlock symbol, or when ever you need to fix a conflicting symbol, use a name of the following format:Generate a UUID, replace the hyphens with underscores, and add a prefix of your choice6.For example,

#if !defined Xa3069ed0_981e_11d1_a9da_0020af6bccd6#define Xa3069ed0_981e_11d1_a9da_0020af6bccd6// ... body of header goes here#endif

For names that are global but must be “user friendly”, refer to Usable #defineNames on page 15.

Rule 2. Use UUID format (preferred) or standard “user friendly” format for global names.

6 The prefix is necessary because preprocessor identifiers can’t begin with a digit. It would not hurt to leave it off when the GUID starts with a letter. But for consistency, I use a prefix X on symbols used for conditional “export” in a DLL, and a prefix I for the include-only-once guard.

C++ Programming Guidelines Page 13John M. Dlugosz 18-July-1999

Page 14: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

DLL NamesThe issues involving DLL names are uniqueness, understandability, and revision control.The name of a DLL should be understandable. A name like MSVCIRTD.DLL is not understandable. A better name would be “Microsoft C Run-time Library.DLL”.The name of a DLL needs to be unique within a program, and avoids additional complexity if they are unique on a system. So, names of DLLs should take reasonable precautions to be unique. I suggest naming the DLL based on company, project, and component, in that order, as whole words or reasonable abbreviations. Notice that the example of “Microsoft C Run-time Library.DLL” follows this format, where C (as opposed to Basic, Fortran, etc.) is the “Project”.The version of a DLL should also be part of the name, as versions may need to co-exist on the same machine. For example, I see MSVCRT.DLL, msvcrt20.dll, and msvcrt40.dll in my system32 directory. I don’t know why MSVCRT.DLL doesn’t have a “50” on the end7, as the older versions have designating numbers. Also, there are variations other than versions, having to do with compilation options. For example, MSVCRTD.DLL is the debug version.I recommend that build options use dotted suffixes after the base name of the DLL (the primary version has no extra suffix), and that version information be contained in curly braces, at the end of the name. For example, this would produce the following names:Microsoft C Run-time Library{4.0}.DLLMicrosoft C Run-time Library.debug{4.0}.DLLSpaces may occur within the base name, as you wish; but there are no spaces between parts or around the dot or brace delimiters.For more on DLLs, see the section beginning on page 25.

7 or why it’s in all caps, for that matter.

C++ Programming Guidelines Page 14John M. Dlugosz 18-July-1999

Page 15: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Usable #define NamesUpdated 8-September-2000

Names that need to be user-readable but are global and subject to conflicts should be named after the DLL they apply to.For example, the macro that controls importing/exporting of symbols could be called XXX_EXPORT, where XXX is the base name of the DLL. (see Standard Structure of a .H file on page 8 for another approach—temporarily aliasing a UUID-based name with a readable name).Some libraries may have special symbols used for configuration. For example, optional namespace wrapping in a project was done with XXX_STARTWRAP and XXX_ENDWRAP, and the #define used to control the conditional compilation was XXX_NOWRAP.The point is, all macros that appear in the public header files that don’t use the UUID format (see Globally Unique Identifier Names on page 13) should be prefixed with the DLL base name.The rationale here is that you can’t have two DLLs with the same base name statically linked into the same program. So, DLL base names are already unique in the translation unit.Furthermore, these names should never be re-used elsewhere in the code. The intention is that in case there is a conflict, a global search and replace can remove the offending name (replace with a UUID-based name) without causing any side effects what so ever.

Reserved NamesDon’t use reserved names in your code. This is covered in the C++ Standard (ISO/IEC 14882:1998(E)) in section [17.4.3.1].A common mistake is to use names beginning with an underscore and capital letter, mimicking the naming convention found in the standard headers for include-file interlocks. E.g.

#ifndef _INC_STDIO#define _INC_STDIO...#endif

The use of such identifiers is reserved for the implementation. So, it’s perfectly correct for the standard headers to use a name like _INC_STDIO for a macro. But it is incorrect for your own header to define _INC_FOO or something like that. The whole point is that the standard library headers use their own names so as not to conflict.For specific advise on the include file interlock issue, see Standard Structure of a .H file on page 8, and Globally Unique Identifier Names on page 13.In general, know and respect these rules:

C++ Programming Guidelines Page 15John M. Dlugosz 18-July-1999

Page 16: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

A name beginning with an underscore and a capital letter or a second underscore is reserved for the implementation for any purpose. By “any purpose”, it could be a macro, so beware of these names.A name that begins with an underscore is reserved by the implementation for things at file scope, or use within the std namespace. So, you could safely define a class member or local variable with such a name because you know that if the standard headers define such a name, it won’t be a macro. But their use is still inadvisable.A name that has two consecutive underscores anywhere within it is reserved for names that have external linkage.Also, anything specifically defined in the C or C++ standard library is reserved. But in this case, it is acceptable to reuse these names for your own purposes, including overloading them at global scope.

Rule 3. Don't use names that begin with an underscore or contain a double-underscore.

C++ Programming Guidelines Page 16John M. Dlugosz 18-July-1999

Page 17: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

CommentsBe Useful

Material from this section adapted from The Guru’s Handbook.Comments should be useful.More specifically, do not tolerate comments that are wrong, that say nothing useful, or otherwise make the code harder to read.useless commentsFirst of all, there are silly comments. If you don't have anything to say, don't say anything! Sometimes the code is quite readable on its own. If it is not, perhaps it could be made more readable in ways other than adding a clarifying comment. Here are some examples, seen in a real program.

1. NumLockOff(); //make sure num lock off2. key = key::inkey(); //get key from user3. shift_status= key::shift_status(); //get shift key status4. two_d_swatch far * probe_reading //far ptr to probe reading

Consider number 1. The comment does not say anything more than the function name did already. If you don't immediately know what "Num Lock Off" means, the comment doesn't help one bit! The sad part is that this comment comes so close to doing something useful. It should clarify what Num Lock is, as well as point out that this call turns it off. Since anyone on this platform would know that there is a NumLock key on the keyboard (similar to CapLock), all the comment needs to do is make the connection stronger and jog the reader's memory. A better comment would be "disable the keyboard NumLock state".Number 2 is better. Whether the comment is useless or not depends on context. If found in a small example program, it can be assumed that the reader knows nothing of the library in use and needs a blow-by-blow description of everything. However, in a major project which makes heavy use of the key class, this comment is useless. A better comment would be to explain why a key is being gotten from the user, especially if it is unusual. Example, getting a key to throw away might have the terse comment of "pause".Number 3 is clearly useless. It combines the worst features of numbers 1 and 2, and there is nothing that can save it. This comment should be deleted.Number 4 demonstrates that bad comments are not limited to function calls. Declarations and other statements can have them too. Consider:

while (!done) { //loop untill doneIn general, don't have a comment just resay what the statement did. The comment should augment and clarify, not simply restate.

C++ Programming Guidelines Page 17John M. Dlugosz 18-July-1999

Page 18: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Rule 4. No useless comments.Over-wordy commentsThe clarify vs. simply restate issue runs into problems when you reach the edge of your mastery of the language. For example, a comment such as:

for (;;) { //infinite loop

would be one of these silly useless comments to anyone who knew that for(;;) looped forever or could trivially figure it out on the spot by knowing how for loops worked. But this comment might be appreciated by a novice who has never seen the construct before.In a professional situation, we can assume that the readers are not novices. Write (and document) code for other professional programmers to read, not for CS-101 students to read.

Formatting Of CommentsMaterial from this section adapted from The Guru’s Handbook.

It is important that the reader know what a comment is referring to. Does a comment explaining a process refer to the last few lines, to the next few lines, or describe a placeholder for code yet to be written? You need to be clear and consistent in positioning comments relative to the code being described.Planned improvement commentsOne issue overlooked is the use of comments for things that don't exist yet. Comments may indicate placeholders for code yet to be written. It can be quite confusing to have a comment that does not describe the surrounding code, but states something that is not being done at this point. So, make such comments clearly indicate that they are placeholders and not description. I do this by beginning such a comment with ">>". For example,

get_data();// >> check for overflowreport_data();

Suggested formatting styleA lengthy “paragraph” comment will use C-style multi-line comment delimiters, and be intended relative to the code:

void classname::funcname (int x){/* this is a long comment that describes the function which wraps along several lines. Not nomally found, as details of what the function does goes in the docs. This would describe implementation notes. */line1();line2();}

Make sure it is clear just what group of code a comment is applying to.

C++ Programming Guidelines Page 18John M. Dlugosz 18-July-1999

Page 19: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

An end-of-line comment should describe that line. A comment on a line by itself and indented to match the surrounding

code will describe the following lines. A comment indicating that something is to be done, as opposed to a comment indicating that the following code does this, must be clearly distinguished or the reader will be very confused. Open such a comment with ">>".

end of line commentcount++; //leave room for end-cap.

line group comment//add item to listnode* p= new node;next= p->next;p->next= this;

planned improvement commentget_data();// >> check for overflowreport_data();

No Verbose CommentsDon’t use a whole paragraph to document a single line of source. If something needs more explanation than fits in an end-of-line comment, use a comment that says

// see note 1or something like that, and put the comment where it doesn’t interfere with the readability of the block. Typically, such a note can be worked into the explanation of the function as a whole, which is OK to be lengthy. Just put such comments between blocks, not mixed in with code. Also see Don’t Mix Code With Documentation, on page 19.

Don’t Mix Code With DocumentationDocumentation is best kept in a separate document file, not mixed in with the source code. I’ve seen many cases of source-code comments that are out of date. Not only do such comments tend to repeat what the code already says (that is, the signature of the function), but it’s a fact that programmers let such comments get out of date. The idea that such internal documentation will be better maintained than external files is empirically shown to be wrong.Comments in the code can explain implementation nuances pertaining to a specific spot in the source code. But documenting a library requires general explanations that don’t go in any one “spot” in the code. Such documentation is best written in essay form, and you should use proper tools for the job, i.e. a word processor.

C++ Programming Guidelines Page 19John M. Dlugosz 18-July-1999

Page 20: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Per-Function ReferenceThe following example comes from a real listing, and is typical of the coding standard used to write it.note: the symbol in a listing indicates a source line that was wrapped to fit on this page. A line break does not exist in the actual code.

//## Documentation: sets whether function exit log entries are recorded

// Parameters: flag - turns on/off recording function exit log entries

// Return Value(s): previous state of recordOnExit

// Side Effects: none

//## Exceptions: none

//## Preconditions: none

//## Postconditions: none

static int setExitRecord (int flag) { int oldFlag = recordOnExit; recordOnExit = flag; return oldFlag; }

The function declaration is rather simple, and being inline (this appears in a class definition in a header), the full body appears as well.But an additional 14 lines accompany this function. Half of them are blank. More than half of what’s left don’t say anything (“none”).The Parameters comment doesn’t say which integer value means what, so it doesn’t provide adequate documentation as it is.The other two comments say things that are already clear from the name of the function and the content of the inline body.If anything, a single line comment would be enough:

// Set the “record on exit” flag, and return the previous state.

This gives as much useful information as the monster formalized comment block does. But, just like the original, it doesn’t explain what a “record on exit” flag is, how it’s used, or why you would want to change its setting.How to call this function is obvious from the code, and a short comment, if any, is all that is required to fill that purpose. The point of having this function, and how to use it in the context of a program, still needs to be explained somewhere!

C++ Programming Guidelines Page 20John M. Dlugosz 18-July-1999

Page 21: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Instead, a proper function reference should be provided in a separate document, not mixed in with the class definition. A “proper” reference would explain how to use the function, not just how to call it.

Rule 5. Don’t put huge lists of formal documentation comments on every declaration.

Per-Class ReferenceHaving a large block of document comments for each class doesn’t hinder readability to the same extent as having a large comment for each member. But if you are going to have documentation comments, say something useful.

//## Class LogReportingSystem

//

// DEFINITION: abstract base class for logging

//

// RESPONSIBILITIES: defines public access for logging

//

//

class LogReportingSystem{

The first line just says the same thing that the subsequent class declaration does. The DEFINITION and RESPONSIBILITIES lines are somewhat less than enlightening. You could delete the whole thing and not miss it.A better comment would be something like this:

/* The LogReportingSystem is an abstract interface that the rest of the library uses to log messages and errors. It should be customized by the application to send such output where you want it.See <whatever section in the reference> for details.*/

Naturally, the brief synopsis given above is also used to introduce this class in the User’s Manual. So why repeat it? The documentation at http://www.dlugosz.com/Repertoire/, for example, contains a glossary of all the classes across several related libraries, with this kind of brief explanation of each, along with a hyperlink to the full documentation.If someone is looking up a class’s meaning, it’s easier to go to an alphabetical concordance than to find the proper header file and look there. If he’s editing the header file in the first place, he probably already knows what the class is for.

C++ Programming Guidelines Page 21John M. Dlugosz 18-July-1999

Page 22: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

In general, don’t use a pre-defined formal list of documentation comments. This only makes the programmer “fill in the blanks” with “none” or something pointless. Instead, write a description in prose that says whatever needs to be said in this case, and nothing more.

Rule 6. Use thoughtful prose, not a laundry list of attributes to document something.

Revision Control InformationMost revision control systems can respond to special comments in the files. For example,

// $Header: $// $Revision: $

When checked out, these will be expanded into information supplied by the revision control system.However, this has a problem with reusable components. Suppose you get a file foo.h from another source. The header is nicely identified as being revision 42. But, upon checking the entire build tree into your own version control system, it reapplies the keywords and says its revision 1. The real meaning of the header comments have been lost.In general, you can’t control keyword expansion on a file by file basis.So, for files that will be distributed and used by others, include revision information in each file, but not in a form that the version control system thinks of as its own.Specifically, upon shipping source files, they should be stamped with information about this shipment. For internal development, the $Header:$ type comments can be used, so that they are automatically updated on each check-out. But when shipping the code, they should be replaced by non-smart comments that won't interfere with other revision control archives.When files are shipped, it is recommended that, at minimum, the beginning of the file stating The project/product to which this file belongs, The release revision information, The date.An example of a minimal information comment is:

//Kodak DICOM Toolkit, Iteration 6 Release 4, 6-April-98It is quite simple to automatically add this comment to all files, as following Perl script illustrates:

$topline= "//Kodak DICOM Toolkit, Iteration 6 Release 4, 6-April-98\n";#edit this line for whatever you want

while (<>) { $name= $_;

C++ Programming Guidelines Page 22John M. Dlugosz 18-July-1999

Page 23: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

open FILE, "<$name" or die "cannot open $name for reading\n"; @content= <FILE>; close FILE; next if $content[0] eq $topline; #already got this file unshift @content, $topline; open FILE, ">$name" or die "cannot open $name for writing\n"; print FILE @content; close FILE; }

Additional information can be handy, in making it clear that the file has not been edited by the customer, but in fact reflects the official shipping version of the file. This kind of information would include the file date/time stamp, the line count, or the length. Anything more elaborate such as a checksum is not worth the effort, as it's just as simple to DIFF against the shipping file as it would be to run the checksum utility to see if the file has been altered. In other words, keep it simple, using values that are trivial to obtain by inspecting the file or its directory entry.It is recommended that all source files shipped contain the minimal information listed above, in a comment that is "fixed" and will not be altered automatically by other tools.

Rule 7. Include project and version identification information in all shipped CPP and H files.

C style vs. C++ Style

Commenting Out Blocks of Codeuse #if 0, not comments.

Rule 8. Use #if 0 to “comment out” blocks of code.

C++ Programming Guidelines Page 23John M. Dlugosz 18-July-1999

Page 24: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Namespaces

C++ Programming Guidelines Page 24John M. Dlugosz 18-July-1999

Page 25: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

DLLsSee also DLL Names on page 14.

Embed Version Information in DLLA DLL should contain embedded version information. This kind of information can be supplied in several different ways. It is recommended that any shipped DLL contain the VERSIONINFO resource, and support the DllGetVersion function, both described below. In addition, information can be embedded in the DLL in other ways, but no recommendations or standards exist at this time.

The VERSIONINFO resourceThe VERSIONINFO resource type is widely recognized. In particular, it is used by many "setup" utilities to decide whether to overwrite an existing DLL, and the GUI shell will show some of this information on a property page for any DLL or EXE that contains this resource. So, it is highly recommended that any DLL contain this resource.A VERSIONINFO resource is easily created by the IDE or other graphical resource editor. Alternatively, you can create a resource script file like this example:file version.rc

#include "..\commonstuff\version.h"

//change these lines for each DLL#define NAME "FOO"#define DESCRIPTION "what foo does"

// what follows should not require any changes1 VERSIONINFO FILEVERSION VERSION_ID //defined in the H file PRODUCTVERSION VERSION_ID FILEFLAGSMASK 0x3fL#ifdef _DEBUG FILEFLAGS 0x3L#else FILEFLAGS 0x2L#endif FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0LBEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", COMPANY "\0"

C++ Programming Guidelines Page 25John M. Dlugosz 18-July-1999

Page 26: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

#ifdef _DEBUG VALUE "FileDescription", DESCRIPTION ", debug build\0"#else VALUE "FileDescription", DESCRIPTION "\0"#endif VALUE "FileVersion", VERSION_NAME VALUE "InternalName", NAME "\0" VALUE "LegalCopyright", COPYRIGHT "\0" VALUE "OriginalFilename", NAME ".dll\0" VALUE "ProductName", PRODUCT_NAME "\0" VALUE "ProductVersion", VERSION_NAME "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 ENDEND

file commonstuff\version.h// This is included by the RC files for the various DLLs, to// define the version information in one place.

// This file contains the official build number.// update these values on each release.

#define VERSION_ID 3,1,4,1#define VERSION_NAME "3.1.4.1, 1997-Dec-19"

// other values (constant for this product)

#define COPYRIGHT "Copyright © 1997"#define PRODUCT_NAME "FooBar Builder"#define COMPANY "Pseuper Pseudocode, Ltd."

The idea is that changes are localized. The actual version identification, which needs to be updated on each release, is located in one place in a single file. This file is included in every DLL in the project. Constant information about the company and copyright is also in this file.Meanwhile, a version.rc file is present in every DLLs source code set. In each DLL, you start by copying the version.rc from another DLL, and then change two lines, conveniently located at the top of the file, to show the DLL name and description for this specific DLL.

The DllGetVersion functionThe version functions that go with the VERSIONINFO data, designed to be used by setup programs, is rather difficult to use. So, Microsoft decided to try again. In most (if not all) of the newer DLLs in Windows NT and Windows 95, each DLL exports a function named DllGetVersion.This is particularly handy for checking the version of a dynamically-loaded DLL before attempting to use its features.

C++ Programming Guidelines Page 26John M. Dlugosz 18-July-1999

Page 27: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

So, include the VERSIONINFO resource so that the GUI shell shows a property page to the interactive user, and include a DLLGetVersion function so that programs can determine if a current-enough DLL was dynamically loaded.

extern "C" __declspec(dllexport) HRESULT __stdcall DllGetVersion (DLLVERSIONINFO* p)

{int val[4]= { VERSION_ID };static DLLVERSIONINFO result= {

sizeof DLLVERSIONINFO,val[0], //major version numberval[1], //minor version numberval[3], //build numberDLLVER_PLATFORM_WINDOWS};

*p= result;return NOERROR;}

This sample implementation uses the same VERSION_ID macro that was defined for the VERSIONINFO resource data described above.

Embedded "what" stringsYou can embed information inside the binary image of a DLL or EXE file in various ways. You can simply have a string literal, static variable, or resource string. Or, you can use a specialized section in the image file.This is popular in the UNIX world, but not recommended here for formal identification of files. It can fill a secondary role, though, for supplying a hidden copyright message in addition to (not in place of) the mechanisms described above.If you define a mechanism known by a particular product suite, you only have to deal with your own files and don't have to worry about making it "open" and obvious to everyone. But even in this case, it is better to have a rigorous way to locate data unambiguously (such as a resource having a specific ID, or an export with a specific name) rather than having to search the file for patterns.

HTML "about" resourceEach DLL or EXE to be identified will have a resource of type RT_HTML (defined as 23) and the name "About"8 used for this purpose. This resource will contain HTML-formatted text that can contain anything the publisher desires. It is intended to be a small "about box" that identifies the DLL and contains a hyperlink to the author's web page or more detailed online documentation.The ability to create or import a HTML resource is part of the IDE in VC++ 6.0. In VC++ 5.0, the resource compiler understands the HTML resource type, so you can include the following in a resource script:

About HTML "about.html"8 "About" should be the actual name of the resource, not a #define that gives the resource a number.

C++ Programming Guidelines Page 27John M. Dlugosz 18-July-1999

Page 28: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

where "about.html" is the file containing the text to be included in the resource, and About is not defined as a macro.A shell extension could be written that makes an "About" tab available under the Properties of image files bearing this resource.The Microsoft Internet Explorer can display this resource by using the "res:" protocol. For example, use a URL of "res://e:\programs\foo.dll/About". The underlined part should be replaced with the fully-qualified name of the image file.

C++ Programming Guidelines Page 28John M. Dlugosz 18-July-1999

Page 29: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Exporting SymbolsUpdated 8-September-2000

When a program is made from multiple image files (DLLs and an EXE), there is an issue regarding finding symbols in other units. In order for one unit, a complete image file in itself, to be linked, it must know that certain symbols can be found elsewhere.In general, a DLL will export all symbols that other units may access, and a DLL or EXE will import all symbols that are being provided by other units.Generally, a common header is used when building the DLL and when using the DLL. That causes problems with Microsoft's syntax, because when building the DLL the symbol must be declared as exported, and when using the DLL the symbol can't be declared as exported. Because of this, we are resigned to using macros and conditional compilation.

General TechniqueYou must have a different preprocessor symbol for each DLL, in order for DLLs to be able to use other DLLs without conflict.To choose a unique name, you can use the lib-prefix technique, as explained on page 15: For each DLL, use a symbol of the form LIBNAME_EXPORT.Or, you can use a UUID-based name and temporarily alias that to PUBLISH only within the body of that header file. This is the technique explained on page 8.In the header files belonging to that DLL, conditionally define the symbol as shown at point in the listing on page 8. Code that includes this file without doing anything special will import the symbols.In each CPP source file for that DLL, write the following line at the beginning of the file, before including anything:

#define unique_export_name __declspec(dllexport)The point of all this is that symbols decorated with the export name (or PUBLISH) will be marked as exported when that DLL is compiled, but will be marked as imported when any other code using this header is compiled.

Declaring SymbolsThe PUBLISH modifier should be placed at the beginning of each declaration that is intended to be exported. For example,

PUBLISH void foo();PUBLISH extern const int maxval;

Semantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with apparently equivalent results. I can recall that occasionally the PUBLISH had to be moved from it's nominal position as the very first word of the declaration in order to get correct results. But in writing this guide, I was

C++ Programming Guidelines Page 29John M. Dlugosz 18-July-1999

Page 30: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

unable to reproduce such a case. Perhaps it's a bug that's been fixed as of 5.0, or perhaps it’s a very subtle problem.

Class MembersThere are two ways to export a class. One way is to export the entire class by putting PUBLISH after the class keyword and before the name. For example,

class PUBLISH C { int foo();public: int bar(); };

This indicates that all members, here foo and bar, are exported. Microsoft's documentation calls this an exported class, and it has three drawbacks. First, it exports all members, even when this is not necessary. For example, you may only need to export the public interface, not the private details. Second, the compiler complains if all the base classes of an exported class are not themselves exported classes. So, you typically end up exporting every member of every class, resulting in a huge export table. Most seriously, it doesn’t work with templates9.A reusable DLL may be written so that most of the code is internal implementation details and very little is in the public interface. So, it’s better to selectively export only those things that you intend to export, or things that must be exported because of dependencies.Here is the same class, only exporting the public bar, leaving the private foo unavailable to the outside world.

class C { int foo();public: PUBLISH int bar(); };

As you see, you simply put the PUBLISH symbol at the beginning of the declarations for the desired members, just as you did with non-members.So what class members do you need to export?In general, only export things from classes that are meant to be used by other DLLs. Classes that are only used internally to the DLL should not be exported. This is the main savings in selective exporting.Of those classes that are known features of a DLL meant to be used by the outside world, the public members obviously need to be exported (unless they are inline).Protected members would be exported too, if you want the outside world to be able to derive from this class and use the protected interface. Withholding protected members from export is one way to restrict the use of a class.

9 This has not been re-tested in VC++ 6.0, since the technique advocated here has worked without problems.

C++ Programming Guidelines Page 30John M. Dlugosz 18-July-1999

Page 31: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Inline functions can cause additional dependencies. For example,class C { int foo();public: PUBLISH int bar(); inline int baz() { return foo(); } };

Anyone calling baz will cause a linker error complaining that foo can’t be found. The user can’t code a call to foo himself because it is private, but the user can call public functions which expand inline and contain calls to private functions.Virtual functions don’t need to be exported in order to call them through the virtual function table. However, they do need to be exported in order to call them in a non-virtual manner.

class C {public: virtual void foo(); PUBLISH C(); };

void sample() { C x; x.foo(); //causes linker error. C& y= x; y.foo(); //works fine! }

Meanwhile, the constructor’s code needs to access all the virtual function’s addresses in order to populate the virtual function table. If the constructor’s code is present in the same DLL, it doesn't care about exports. But, a constructor that is inline will be subject to the same issue as explained above for inline functions. An inline constructor, or a constructor that’s implicitly generated by the compiler, will require that all virtual functions (public or private) to be exported.

TemplatesA template is generally instantiated in each translation unit, and then the linker purges duplicates. However, each DLL is a complete linked unit unto itself, so the duplicate merging does not span across DLLs. Instead, each DLL will contain its own copy of the instantiated class.You can prevent needless duplication (as well as conceal implementation details) by putting instantiations inside a DLL and exporting them, so that other images use the copy inside that DLL. This explicit instantiation is done just as with “normal” code (and not explained in detail here), but you also need to export the instantiated code.To do this, put PUBLISH qualifiers on the various member functions that will be needed, just as with non-template classes. The definitions of the template

C++ Programming Guidelines Page 31John M. Dlugosz 18-July-1999

Page 32: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

functions don’t go in the header, as other translation units will not be instantiating the class.Your specified point of instantiation, inside the DLL, needs to know the definitions of the template functions. The instantiated functions will be exported because of the PUBLISH on the member in the template. So, it works just like explicit instantiation should, but correctly crosses DLL boundaries.Besides causing excess code, duplication of static members can cause problems. So, sometimes you need to ensure that a particular specialization is only instantiated in a single DLL, or failing that, that any code that actually access that class goes through the same DLL.

C++ Programming Guidelines Page 32John M. Dlugosz 18-July-1999

Page 33: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

DLL-specific ResourcesThe code in a reusable DLL may want to use resources such as string tables, bitmaps, and dialog box templates. In order to make the DLL self-sufficient, these resources need to be part of that DLL. Since each DLL is an independent image file, each DLL has its own set of resources.However, code in a DLL needs to understand that the DLL has different resources than other DLLs which are different from the main EXE's resources. The bottom line is that any call to a Win32 API function that obtains resources from the image needs to use the correct instance handle10 in the call, referring to that DLL. For example,

int result=DialogBox (DllInstance, dlgTemplate, owner, dlgProc);

Here, the DllInstance passed to DialogBox needs to be the instance handle of the DLL containing the dialog template, which would be the same DLL that contains this function call.Some functions can use a null value for the instance handle. Some interpret a 0 to mean the main program (that is, the EXE file, not any of the DLLs), and others interpret a 0 to mean a built-in or stock resource not associated with the program. So, when writing code for a DLL, make sure you always use the proper instance handle for that DLL, anywhere an instance handle in needed.There is no straightforward way for code to get the instance handle of "this" DLL that contains the code doing the asking. Instead, each DLL should keep its own instance handle in a global (but not exported) variable. Any code in the DLL then refers to this global value. Code in a different DLL refers to a different global variable, even if it has the same name.Each DLL is told its instance handle when it is loaded. So, you can set this global variable in DllMain the first time it is called. However, static constructors are called before DllMain, so this is not always suitable. For a fully general solution, use the _pRawDllMain hook as explained on page 34.

Rule 9. Always use the correct instance handle when writing code for a DLL.

Initialization and Shutdown IssuesThere are a number of issues that you should be aware of regarding DLL initialization and shutdown. In addition to any explicit DllMain function you might supply, note that objects of global lifetime (specifically, global variables and static members) have their constructors called as part of DLL initialization.

10 In 16-bit Windows, instance handles and module handles were both associated with a DLL. In 32-bit Windows, there is only one value that is used in both roles.

C++ Programming Guidelines Page 33John M. Dlugosz 18-July-1999

Page 34: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

The Process Critical SectionThe DllMain functions are serialized. There is a CriticalSection that prevents a DllMain from being called from another DllMain, so creating threads as part of the startup code, or anything in the DllMain that blocks on some kind of mutex, can cause deadlocks. (See the section "The Hidden Critical Section" in Matt Pietrek's Under the Hood column in the January 1996 issue of Microsoft System's Journal.)Since it is not documented what API calls might acquire this process-specific critical section11, you need to be very careful in writing the DllMain code, or anything called from it such as any constructors for static members.For maximum robustness (and to ensure correctness under other versions of the OS) you must assume that any Win32 API function could possibly try and acquire this critical section. To prevent deadlocks, you must prevent any other locks from causing a circular wait. Specifically, nothing that's called via DllMain should ever wait for something that's (potentially) signaled by another call from DllMain.As a corollary, you can get a deadlock if a destructor (or function called from the destructor) waits for a thread handle to be signaled (or uses one of the functions that acquires the process mutex), and that destructor is for a global object in a DLL that is unloaded with FreeLibrary. So, sometimes, depending on what the user does with it, an object's destructor can cause a deadlock. This is a general concern for implementing objects that control worker threads [[ cross reference a treatment on that ]].

Threads During ShutdownYou might expect the Process Critical Section to cause an analogous problem during shutdown, and code appropriately. However, there is a worse problem. Apparently, threads other than the main thread (the one that called exit or returned from main) don’t run at all. See Threads and other Operating System Problems [during shutdown] on page 53

Load and Free OrderingThe order of initialization of the DLLs is undefined. All you know for sure is that all statically-linked DLLs will have DllMain called before the main EXE program's startup code is called.

Microsoft's Startup HookUse this "startup hook" to obtain the HINSTANCE value for use in static constructors of DLLs.[Overview and sample goes here. Cross reference to publications.]

11 The above 1996 article states that GetProcAddress, CreateProcess, GetModuleFileName, LoadLibrary, and FreeLibrary all acquire this critical section. This is not a complete list, and the details may vary from release to release!

C++ Programming Guidelines Page 34John M. Dlugosz 18-July-1999

Page 35: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

General Ordering NotesWhen a DLL is loaded, initialization takes place in this order on Microsoft Visual C++ 5.012. Simple constants come in with the code's image. Call _pRawDllMain, if it exists. See Microsoft's Startup Hook above. Allocate an initial "onexit" table, and call all the static constructors. Each

static constructor adds an entry to the "onexit" table for the matching static destructor.

Call your DllMain function.Likewise, for shutting down the steps, in order, are: A redundant request to DLL_PROCESS_DETACH is harmlessly ignored. A redundant request is ignored again. Call any functions in the "onexit" table. Free the memory for the "onexit" table. Call _pRawDllMain, if it exists. See Microsoft's Startup Hook above.In general, it's best to use static constructors and destructors for startup and shutdown behavior. The other mechanism have two semantic differences: the system arguments (HINSTANCE and reason) are provided, and you can count on being called first or last.

12 See the listing for the _DllMainCRTStartup function in CRTDLL.C in the compiler's startup code. This function is linked with any DLL you create, and is the "real" entry point as far as the linker and the operating system is concerned.

C++ Programming Guidelines Page 35John M. Dlugosz 18-July-1999

Page 36: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

LinkingThis discussion is specific to Microsoft Visual C++ and the IDE.There are several ways to specify at build time that a specific DLL will be used. The simplest is to simply include the DLLs project as a dependency in the project. This is simple and convenient when the same programmer is also building or re-compiling the DLL. The project file automatically picks up the correct DLL name based on the dependant project.But if the DLLs project files will not be in your workspace, you need to specify the import lib directly. This can be done by adding them to the Input list under the Link tab in the Settings. Alternatively, a clearer way is to add the import LIB to the project as a source, but it is more difficult to maintain multiple configurations (e.g. debug mode vs. release mode). Either way, the drawback is that the exact name of the import library must be given, and this name will change when the DLL is updated.To make it more customer-friendly, use the method described below to automatically link the DLL into the target, so the name of the DLL is not mentioned in the project file. The name is only mentioned in the source code which is shipped as part of the component, so it automatically reflects the version of the component.This is how the system libraries and MFC is linked in. You don’t need to specify them in the project—they just show up automatically.The following syntax will specify that the import lib will be incorporated into the program:

#pragma comment (lib, "libraryname");This can be packaged in a friendly manner by having a header file called "autolink.h", among the header files for that component, contain such a pragma.For programs that want to dynamically load a DLL rather than link to it, you do not want this pragma included. That's why it does not appear in the normal headers as a side-effect of using the component. Instead, it must be explicitly pulled in (typically in the main module of the target) where desired.To satisfy the dynamic loader's needs, there should also be a character string variable provided that contains the library's own name. This can be a global (within the component's namespace, that is) variable in with other "global" things concerning the component, or in a header of its own. For example,

static const char DLLname[]= "libraryname";It should not contain a path or an extension. The user of the library can use this string as an argument to LoadLibrary, or related purposes. Note that the complete definition must be in the header—it would be pointless to import this symbol!

C++ Programming Guidelines Page 36John M. Dlugosz 18-July-1999

Page 37: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Compiler OptionsThere's more than one way to build a program. This section covers issues related to compatibility among different options, and details on specific option choices.

Standard Build OptionsWhen building a project containing multiple DLLs in addition to an EXE, it is important that all the modules are built with the same (or compatible) options. When building DLLs for general reuse, it is important that some wider standard for build options exist.

Debug OptionsUnfortunately, Microsoft's header files contain conditional compilation based on the _DEBUG symbol. This conditional compilation renders code compiled in "debug mode" incompatible with code compiled in "release mode". In addition to disabling optimizations (that would make code more difficult to step through) and generating symbolic information, which is what you expect from a debug build, Microsoft insists on changing the layout of system structures so that things like standard IO and malloc/free are incompatible. Linking OBJ files with mismatched _DEBUG state will usually result in no errors, but the program will do strange things when run.So, you need to ship two distinct builds of a reusable DLL: a regular version (release mode), and a debug version. Keeping two distinct versions, as opposed to coming up with standards that let a single version do, will allow you to continue the trend of having extra error checking (even to the point of incompatibility with the regular build) in the debug build.Furthermore, you should always ship PDB files containing symbolic information, even for release-mode DLLs. This allows us to track a crash point back to the offending area in the source code. Note that the PDB's don't need to be shipped with the final product—rather, a programmer can use the PDB file to make sense of the Address given in a fatal error message. So when writing a DLL for reuse, the programmer that reuses the DLL should have access to the PDB file, though he does not pass it along to the end user. For more on this topic, see John Robbins' Bugslayer column in the April 1998 issue of Microsoft Systems Journal.

C++ Language OptionsOn the C/C++ Settings tab in Microsoft C++ 5.0, the standard settings to use are as follows:Pointer-to-member representation method: Best-Case Always (the default, specified with /vmb). The layout and interpretation of a pointer to a data member can vary based on compilation options (pointers to function members are always done through thunks). If one translation unit creates a

C++ Programming Guidelines Page 37John M. Dlugosz 18-July-1999

Page 38: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

pointer using one method, and a different translation unit dereferences the pointer using another method, it won't work properly. Likewise, the layout of data structures can change, as the size of the pointer to member would change.So, all code must agree on the setting for this option. The default automatically chooses the best method for the class, but requires knowledge of the class before a member pointer can be declared. If this is not acceptable, use Microsoft extended keywords __single_inheritance et.al. to declare the nature of the class prior to its definition.

Enable exception handling: set (/GX). DLLs must all be set or clear in order to be compatible with each other. "Set" is correct in C++; "clear" is only for compatibility with old DLLs that were compiled with a different layout.Meanwhile, the /GX option (supplied in the Settings dialog for the project) is equivalent to /EHsc in version 5.0. The newer /EH option allows more precise specification of how exceptions behave. In the 4.2 version of the compiler, /GX did the same thing that /EHa does now, which is different. It's possible that code that worked in 4.2 will fail in 5.0 because of this change. For source files that require more robust13 but less efficient exception handler code, use /EHa instead. If you have extern "C" functions that throw exceptions, you must get rid of the /EHc option. In general, each translation unit can be compiled with whatever /EH option it likes, as long as all code has exception handing enabled.It could be argued that reusable code should always be compiled with /EHa, not the default /EHsc, but a study of the benefits and overhead has not been performed. If you expect to catch exceptions thrown by division by zero, stray pointer dereference, or anything other than a throw statement, you need /EHa.Enable Run-time type Information: set (/GR). RTTI is part of the C++ language, and may be used in a program. Code compiled without /GR is incompatible with code that is, so you must standardize on /GR.Disable construction displacements: clear (the default, specified with /vd1 or the lack of /vd0). I don't know exactly what this does, but the default appears to be the semantically correct case. I don't know what the ramifications are when mixing code with different /vd settings, so don't do it to be safe.

Code Generation OptionsCalling convention: __cdecl (the default, /Gd). This option does not affect member functions, and non-members should be a minority of the code. If you want otherwise, use explicit calling convention keywords on each signature, rather than changing this option from the default.

13 For example, the compiler doesn't take into consideration that dereferencing a pointer could throw an exception. If you catch this exception, some of the innermost variables may not be destructed.

C++ Programming Guidelines Page 38John M. Dlugosz 18-July-1999

Page 39: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Use run-time library: Multi-threaded DLL or Debug multi-threaded DLL. This is not the default when creating a project in the IDE, so be sure to change it!Struct member alignment: 8 bytes (the default). Obviously, all code must agree on this. If the default setting is not suitable, use a #pragma to specify alignment on a case-by-case basis, and restore the default setting before the end of the header file.

Options for Efficient CodeThe items listed here do not affect the compatibility of the generated code, so these are guidelines rather than strict rules for reuse. However, wise choices can affect the performance and size of the resulting code.Much of this advise is taken from Matt Pietrek's article Remove Fatty Deposits from Your Applications Using Our 32-bit Liposuction Tools in the October 1996 issue of Microsoft Systems Journal.

Turn off incremental linking.Debug information should be in a separate PDB file, not embedded in the EXE or DLL file. As explained in Debug Options on page 37, you can supply debug information even in a "release mode" build. But you don't want the file to be bloated because of it, and you don't want this information distributed to end users. With debug information in a separate PDB file, the overhead present in the EXE (or DLL) is trivial.Eliminate FPO records from the executable.Use compiler optimizations.Don't use the /Gy option (individually packaged functions). Note that /Gy is implicit in both /O1 (optimize for size) and /O2 (optimize for speed). However, /O1 automatically turns on /Os too. /Os changes alignment of the packaged functions to 1, mitigating the problem with /Gy introducing excessive padding between functions.The bottom line: don't use /O2 (speed), but specify individual optimization flags. Using /O1 (size) is OK.

Options for Programmer ConvenienceLink IncrementallyIf your link time is significant, you might consider this option, but remember to turn it off before shipping. If your link time is very small compared to other parts of the build process, don't bother.

C++ Programming Guidelines Page 39John M. Dlugosz 18-July-1999

Page 40: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Enable minimal rebuild (/Gm)This causes the compiler to perform dependency checking based on individual classes rather than on whole header files. It ought to be a God-send to certain folks who have tangled masses of interdependent include files. But, the IDE can't even get ordinary dependency checking right, and has a tendency to not recompile things that ought to be recompiled. Being bitten once or twice more than pays for any time saved by using this option. For those of us who write clean decoupled modules, this option ideally offers no savings at all, and is not worth the problems it causes.

Enable incremental compilation (/Gi)This causes the compiler to recompile only those functions that have changed. For reasonable sized source files, I've found no time savings in using this option.

Precompiled headers (/YX, /Xu, /Xc)Precompiled headers can dramatically improve compilation time for MFC programs and sometimes for Win32 programs that include <windows.h> consistently as the first inclusion.For projects that have more reasonable sized include files, and have source files that include different headers in different orders, precompiled headers don't help at all.Furthermore, the use of precompiled headers have been seen to cause strange rebuild problems, such as erroneous compiler errors and ignored changes to source code.

Run-Time Library in a DLL or Statically Linked?

C++ Programming Guidelines Page 40John M. Dlugosz 18-July-1999

Page 41: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Error HandlingWrite Pro-EH CodeThe pro-EH philosophy is “A function will do what you tell it to do, or it doesn’t return at all.” See An Exceptional Philosophy in WinTech.no explicit cleanup required

Exception SpecificationsFunctions Return Resultsas opposed to returning an error code

Constructors and Destructorssee Classes.

Catch ReferencesDon’t Throw PointersUse Standard Exception Types

C++ Programming Guidelines Page 41John M. Dlugosz 18-July-1999

Page 42: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

InternationalizationDon’t Assume the Whole World is Latin-1

updated 17-June-1999The biggest thing you can do with respect to making your program globally available is to simply think about it.Even if your program doesn’t have its user interface translated into other languages, you would still expect the program to run in a different country, not crash and burn.If you use Unicode, this should not be a problem. If you use 8-bit characters and have the wrong code page, you might display the wrong characters or mis-identify what is a letter and other cosmetic issues. But if you ignore the possibility of multibyte characters, things can go seriously bad.

Rule 10. Consider users that have different character sets.

Watch Out For Multibyte CharactersIn Far East localized versions of Windows, and undoubtedly in other operating systems as well, the current locale can contain a multibyte character set. For example, the C++ string "\x41\xA3\x5C\x42" is four bytes. In code page 1252 (Windows Latin 1), this means “A£\B”, or LATIN CAPITAL LETTER A, POUND SIGN, BACKSLASH, LATIN CAPITAL LETTER B. In code page 950 (Hong Kong and Taiwan), this means “AαB”, or LATIN CAPITAL LETTER A, GREEK SMALL LETTER ALPHA, LATIN CAPITAL LETTER B.So a user in Hong Kong (a place that is highly industrialized and probably uses a lot of software) will enter this 3-character string. A naïve program will think it’s four characters, not three. Maybe that’s not a problem, if the program cares about bytes not characters per se. But, if the program scans the string for a backslash character, such as when parsing a file path, it will incorrectly see the second byte (0x5C) of a multibyte character as a backslash. This is not cosmetic, but can lead to strange program behavior.

int pos= strchr (buffer, '\\'); //wrong!

The Microsoft compiler comes with a C library function called _mbschr that handles characters in the current locale properly, even if there are multibyte characters. What does the ISO C library have for this?Or, you could document the fact that users in Hong Kong can’t use an alpha (and bunches of other characters that end in the wrong byte) in file names. You would need a different list for each multibyte character set.

C++ Programming Guidelines Page 42John M. Dlugosz 18-July-1999

Page 43: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Distinguish OEM and ANSI Character Setrevised 7-August-2000

In Win32 operating systems that support both -A and -W forms of functions, normally the -A form uses the current ANSI code page. However, functions exported from kernel32.dll that take or produce file names can be set14 to use OEM character set.This means that if you call the so-called “ANSI” form of FindFirstFile, and the first file is named naïve.txt, the output buffer will contain a \x8b byte for the i-with-diaeresis. If you then pass this string to the ANSI form of MessageBox, it will show the file name as na‹ve.txt, because the code \x8b means SINGLE LEFT-POINTING ANGLE QUOTATION MARK. FindFirstFile, on this American computer, used code page 437, while MessageBox used code page 1252. The correct code for the double-dotted i is 0xEF.You can switch the behavior of the file-name-related functions to use ANSI rather than OEM code pages, but this mode affects the entire process. So if you switch to ANSI, call the function, then switch back to what it was, other threads can still be confused when the setting suddenly changes on them.Similar issues exist with the Console functions.The simplest solution is just to use Unicode. If the -W versions of the functions are not available, then you must note which code page they are using and make allowances for it.You can’t just convert from OEM to ANSI because there are characters in one that are not in the other. For example, the block and line-drawing characters found in the OEM character sets have no counterpart in any ANSI character set.On my American NT machine15, experiments show that a file name containing a lower-case Greek delta symbol (δ) was noted correctly when File API was set to OEM, but was translated into a lower-case d by FindFirstFileA when set to ANSI. The name returned by FindFirstFileA could not then be opened by CreateFileA ! Conversely, a filename containing a double dagger (‡) was worked correctly when File API was set to ANSI, but produced a strange result from FindFirstFileA that again could not be used to open the file with CreateFileA.Basically, unless the characters making up the file name all appear in the currently selected code page, you can’t access the file using the –A versions of the functions.Experiments indicate that under NT and Windows 2000, just use the –W forms and don’t worry about it. Under Windows 98, set to OEM to read all files on FAT media (since the directory entry uses the OEM character set), and use the short-name alias for Unicode media such as CD’s. Note that Win9x doesn’t

14 An earlier edition said this was the default. That was either incorrect or out of date. Testing (see Appendix) under Windows NT 4 SP6a and Windows 98 confirm that ANSI is the default. See CheckOEM listing on page 88. The command-line shell seems to use the OEM character set for File API’s, though.15 NT4 SP6a, using NTFS for the test.

C++ Programming Guidelines Page 43John M. Dlugosz 18-July-1999

Page 44: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

always produce an alias name—the file name could be less than 8 characters long but still contain characters that are not in the ANSI code page.

C++ Programming Guidelines Page 44John M. Dlugosz 18-July-1999

Page 45: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

C++ Language UsageCastingDon’t use casts where none are needed. For example:

short x= (short) 5; //unnecessary cast

Rule 11. Don’t use casts where none are needed.When you do need to cast, use the function-style casts, not the old-style casts.When downcasting class pointers or references, always use dynamic_cast, not static_cast.

Rule 12. Use dynamic_cast when downcasting.

Error Checking is Good

Avoid MacrosMaterial from this section adapted from The Guru’s Handbook.

Avoid the use of #define. This was heavily used in C for macros and manifest constants, but in C++ there are better ways.#define is bad because it doesn’t follow scope or namespace. A name #defined will prevent use of that name anywhere thereafter. A const will not interfere with structure member names and other classifications of names, and the names can be re-used on inner scopes. In addition, the C++ techniques discussed below have additional advantages, such as type safety.Manifest ConstantsIn C++, use const variables instead. These are different in C++ than in C, and in C++ are more suitable for general use as manifest constants. A const of some integral type can be used in constant expressions, so they may be used as array dimensions and such. This covers the most typical uses. Compilers now support initialized integral constants within the class definition, so the old enum trick is probably not needed.

// this appears in a headerclass C {public: const int size= 5; int array[size]; };

// this appears in one C++ file

C++ Programming Guidelines Page 45John M. Dlugosz 18-July-1999

Page 46: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

const int C::size; //initializer not repeated.

One place where a const doesn't give the same flavor as a #define is with string literals. For simple character arrays, definitions like char* const s= "hello!"; works just fine. But strings used with #define can be concatenated at compile time into longer strings. Example:

#define ESC "\x1b"puts ("this " ESC "C" "string contains an ESC-C in it.")

Macro FunctionsInstead of a function-style macro, use an inline function. Inline functions overcome several problems with macros: they may be overloaded, the arguments are typesafe, and there is no problem with multiple execution of the formal parameters.Sometimes you want a macro that is not strongly type-checked. These are typically candidates for template functions. For example,

#define max(x,y) ((x)>(y)?(x):(y))becomes:

template <class T>inline T max (const T& x, const T& y){return x>y ? x : y;}template <class T>inline T& max (T& x, T& y){return x>y ? x : y;}

It takes 10 lines compared to 1 for the macro, but sometimes that is the price we pay for good style. Two complete templates are needed to provide for using max() on the left-hand side of an assignment (or other lvalue uses). The second form does this. The first form is for use when either argument is a temporary or has to be converted to a common type. Neither suffers from double-execution of one of the parameters like the macro, and because they are inline it shouldn't generate more code than the macro did.Other uses of #defineThe place where #define is really the correct construct is the use of conditional compilation. When using this, just make sure you don't pick names that might conflict with other names (see Globally Unique Identifier Names on page 13).Another use is what I call non-syntactical macros. That is, things that are neither function style nor simple constants, but are nothing that looks like C++ syntax. Using non-syntactical macros should in general be avoided, but they do come in handy.

C++ Programming Guidelines Page 46John M. Dlugosz 18-July-1999

Page 47: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

An example of a non-syntactical macro I've used is to initialize a large table. To initialize an array with 500 repetitions of the constant 10, a set of macros can cut down on the size of the source code (and the typing!).

#define TEN 10,10,10,10,10,10,10,10,10,10#define HUNDRED TEN,TEN,TEN,TEN,TEN,TEN,TEN,TEN,TEN,TENint bigarray[500]= { HUNDRED,HUNDRED,HUNDRED,HUNDRED,HUNDRED };#undef HUNDRED#undef TEN

In real code, I'd put each of the 5 HUNDRED's on its own line to prevent the expanded line from getting too long. ANSI C says that compilers will take lines that are 509 characters long.

Rule 13. Avoid preprocessor macros in favor of other language mechanisms.

Use Smart Pointers

Use Value Semantics…avoid lifetime issues in general

Promote Strong Type Checking

Use RTTI, Not Home-Brew

C++ Programming Guidelines Page 47John M. Dlugosz 18-July-1999

Page 48: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Memory ManagementMatch frees with allocationsreference article from Cobb Group.Some ways of allocating and freeing memory:

1 T* p1= new T;delete p1;

new-expression with single item.

2 T* p2= new T[20];delete[] p2;

new-expression of array.

3 T* p3= ::new T;::delete p3;

new-expression with single item, ignoring any class-specific operator new()

4 T* p4= ::new T[20];::delete[] p4;

new-expression of array, ignoring any class-specific operator new()

5 T* p5= new (foobar) T;// how to delete ??

new-expression with placement syntax—calls custom version of operator new(). You wrote it, you document the proper way to free the memory, too.

6 T* p6= (T*)malloc (128);free (p6);

C-style malloc()

7 T* p7= (T*)operator new (128);operator delete (p7);

direct call to operator new() primitive, similar to malloc().

8 T* p8= (T*)GlobalAlloc (GMEM_MOVEABLE, 128);

GlobalFree (p8);

Windows 16-bit style allocation.

9 T* p9= (T*)HeapAlloc (myheap, 0, 128);

HeapFree (myheap, 0, p9);

Windows 32-bit style suballocation.

10 T* p10= (T*)VirtualAlloc (0, 128, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

VirtualFree (p10, 128, MEM_DECOMMIT);

Windows 32-bit style large block allocation primitives.

Exceptions vs. NULL for ‘new’An ordinary new-expression does not return NULL. Rather, it throws a std::bad_alloc exception on failure. See ISO C++ Standard §18.4.1.1 and §18.4.1.2.

C++ Programming Guidelines Page 48John M. Dlugosz 18-July-1999

Page 49: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

The rule is: Write code that assumes that operator new has standard behavior16. When putting together a main program (or a DLL), use an operator new that indeed has standard behavior. This advise was supposed to be against including customized behavior in replacement operator new functions or relying on such in reusable code. But as it turns out, you need to supply a correct customized operator new to replace the broken one that came with the compiler.It seems that the Microsoft Visual C++ compiler versions 5.0 SP4 (CL.EXE version 11.00.7022) and 6.0 SP1 (CL.EXE version 12.00.8168) is seriously buggy. Here is a sample program to illustrate the problem.

#if !defined NATIVE //user might specify on the command line#define NATIVE 1 //no special precautions taken. (overrides all other flags)#endif#define MSC_HANDLER 1 //install Microsoft's non-standard new handler

#include <iostream>using std::cout;using std::endl;

#if !NATIVE && MSC_HANDLER#include "new.h"int my_new_handler(size_t) { // What the default is =supposed= to do (see 18.4.2.2 of the ANSI standard) throw std::bad_alloc(); return 0; }#endif

void trial (size_t x) { try { char* p= new char[x]; cout << "new of " << x << " bytes returned " << (void*)p << endl; delete[] p; } catch (...) { cout << "new of " << x << " bytes threw an exception." << endl; } }

int main() { #if NATIVE cout << "Compiled for \"native\" (no special precautions) mode, according to the Standard." << endl; #endif

16 Don’t check the return value from a normal (non-placement) new-expression. If you really want to check for NULL because you prefer this behavior or don’t trust the implementation of operator new, then use the new (nothrow()) T form instead.

C++ Programming Guidelines Page 49John M. Dlugosz 18-July-1999

Page 50: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

#if !NATIVE && MSC_HANDLER cout << "Installing Microsoft's non-standard new handler" << endl; _set_new_handler (my_new_handler); #endif const size_t Meg= 1048576; trial (500*Meg); trial (999*Meg); trial (2000*Meg); trial (3000*Meg); trial (4000*Meg); trial (-1); trial (-31); trial (-32); return 0; }

You can compile this with different compilers and different compile-time options to see just what the behavior is. For Microsoft’s compiler, the “correct” version of the function is found in crt\src\newop.cpp, and should be linked in if STL or any of the new-style (dotless) headers is included anywhere in the program. However, it doesn’t work, and presumably was never tested, because the incorrect version found in crt\src\new.cpp is always linked in, even if the Standard libraries are requested.A partial fix is to supply a new-handler that does what should be done by default—namely, throw an exception. Note that the new-handler signature is nonstandard, though.This is only a partial fix because the underlying code for small-block memory management (function _nh_malloc in the run-time library) begins with a statement that looks like this:

void* _nb_malloc (unsigned cb, int flag) { if (cb > 0xffffffe0) return 0; // do the actual work… // …

In other words, for some values it never engages the new-handler logic, but always returns 0 without further thought. This happens when small negative values (in the range –1 through –31) are accidentally used, and this happens to be a common computational error for calculating the size of things dynamically. So even setting the new-handler is not a perfect solution. It simply doesn’t work in all cases!The fix I’ve found is to include a properly-written operator new and operator new[] in each program or DLL, and make sure it actually gets used, rather than suffering the same fate as crt\src\newop.cpp. For a discussion of the technical details and the function in question, see the Classics library (insert URL here). For more on the bugs in Microsoft’s memory management, see (cite articles here).

C++ Programming Guidelines Page 50John M. Dlugosz 18-July-1999

Page 51: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

C++ DeclarationsAvoid Global Constructors and DestructorsAlso known as “static constructors”, objects with global lifetime (which include static data members) are constructed before the main program starts, and are destructed upon shutdown. Static variables inside functions are constructed when first encountered, and destructed upon shutdown. These have several problems:

Order of Initialization Error Checking Mandatory Start-up Threads and other OS Problems

Order of InitializationThe order of initialization problem can cause dependency problems between code that’s called as part of static construction and other global objects. In some cases, such as the std::cout standard object, you really do want this can use a #pragma to ensure early construction.Note that the use of DLLs compounds this problem. Objects in DLLs are constructed before those in the EXE, but the order of startup of the various DLLs is unknown.The other two issues are more along the lines of real problems with reusable code, rather than the order problem that is a gotcha to be aware of.

Error CheckingThe initialization of a global object does not take place in a try{} block, so any exception will terminate the program with a cryptic error. The code (other parts of the component, or the user of the component) can’t monitor the status and cope with it, or even provide a better error message. Because of this, using non-trivial static construction code is unacceptable.A way around the error checking problem is to use a function call as the initializer. For example:

T foo() { try { T retval (5, 27, 9); return retval; } catch (...) { //do something } }

C++ Programming Guidelines Page 51John M. Dlugosz 18-July-1999

Page 52: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

T global = foo();

This allows the real work to indeed be done inside a try{} block, so the error can be reported in a meaningful manner.For class types, a more efficient alternative is to use a special purpose derived class. The derived constructor contains the error checking. The user of global doesn’t care that the type is actually U since U “isa” T.This requires that a function try block be used, as the base class initializer is called before the body of the constructor is executed. Putting a try{} inside the body would not do the job.

#include <iostream>

class B {public: B (int x) { throw 2*x; } };

class C {public: C (int x) try : B(x) {} catch (int y) { std::cout << "caught " << y << std::endl; } };

C global (5); // I really want a B.

int main() { std::cout << "this line is never reached" << std::endl; }

The program still terminates without calling main(), but your own handling code (shown in bold) is called, so meaningful error information may be reported to the user.It has the advantage over the more general technique below of not needing to handle multi-threaded situations.Note that this chokes MSCV++ 5.0, so it’s not a viable option at this time.

Mandatory Start-UpUpdated 5-January-2001

Such globals, especially globals used to perform library-wide initialization, are always initialized. Customer feedback has indicated that they don’t want this overhead (setup time and resource allocation) for parts of the library that they are not using. So, it is better to initialize on demand rather than initializing everything early. At the very least, it spreads out the setup time rather than causing a long delay before anything apparent happens.

C++ Programming Guidelines Page 52John M. Dlugosz 18-July-1999

Page 53: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

This concern is applicable to initialization and shutdown code that consumes resources or time, or might by its presence affect other things that the user may need to customize.To these two ends, it is recommended that such global objects be created at first use rather than at program startup. A simple way to do this is to replace the variable with a function that returns a reference:

T global (5, 27, 9); //unacceptable

// replace with this:T& global() { locker L (object_lock); //start a critical section17

static T xx (5, 27, 9); return xx; }

Threads and other Operating System ProblemsA static constructor found in a DLL will have problems starting worker threads, as explained under The Process Critical Section on page 34. A more serious problem is that threads and mutual exclusion mechanisms are unreliable during static destructors in a DLL, which are called after the main program calls ExitProcess.Experiments indicate that during shutdown, worker threads never run. So, any cleanup code that the worker thread is expected to perform will not be done. If the main thread is waiting for the worker to do something, it will deadlock. If the main thread that attempts to wait on the worker thread handle itself, it will wait forever under Windows 95, or immediately see a signaled state and not wait at all under Windows NT.Executing under the MSDEV debugger, the symptoms are more varied and more bizarre. In any case, the behavior is different for different Win32 platforms and is not what is documented for ExitProcess. This suggests that the cautious programmer should avoid relying on operating system features in general during shutdown. Note that some OS features such as COM and WinSock use threads internally, which means that this specific problem may cause other things to fail during shutdown.

SummaryAny global variable or static data member should be written with these considerations in mind. A simple built-in data type being initialized with a constant expression is never a problem18.Types with constructors or variables with non-trivial initialization need to consider dependency issues and error-trapping issues.17 A critical section is needed around local static objects, as explained under Static Local Variables on page 70.18 Actually, it has been observed that Microsoft's compiler once treated a constant expression as a run-time computation. Details of the bug have not been investigated.

C++ Programming Guidelines Page 53John M. Dlugosz 18-July-1999

Page 54: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Objects that exist only to cause initialization as a side-effect, or objects that exhibit asynchronous behavior, or objects that heavily consume resources or time during construction or destruction need to consider the unwanted mandatory start-up issue.Objects that control worker threads should be prepared to abandon the threads in their destructors rather than rely on proper termination, and should detect this case without deadlocking.

Rule 14. Avoid static constructors and destructors.

Declaring Const ObjectsThe language specification has changed, so global const objects no longer have internal linkage by default. Use static when declaring all objects with internal linkage, even if they are const.Of course, the use of static to keep things local to a file is deprecated anyway. Use anonymous namespaces (see page 56) instead.This means that the following code, which has been common practice, is now incorrect.

//header fileconst int max= 5;

Now you must say//header filestatic const int max= 5;

exactly as with the case of other variables. That is, const is not a special case anymore. It used to be static by default, now it works just like any other variable.

Rule 15. Qualify global const variables with static or extern.

Old compilers will see a plain const as static, but new compilers will see a plain const as extern. So never write a plain const, but always specify static or extern to match your intent.

Choosing Names…avoid prefix, don’t restate the object name

Boolean variablesThe C++ language, unlike C, has bool as a built-in distinct type. Use it. Use the reserved words bool, true, and false in your code, not macros or other declarations for BOOL, etc.

C++ Programming Guidelines Page 54John M. Dlugosz 18-July-1999

Page 55: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Rule 16. Use the real bool type, not a substitute.

Caveat:When interfacing with DLLs or other code that you did not compile, note that functions declared as returning bool may only provide one significant byte, even though it returns a whole word physically. This can confuse code linking to it that declares that function as BOOL where BOOL is really int.

Enumeration Typesrevised 5-January-2001

Class-scoped constantsDon’t use enum to make class-scoped constants, when the compiler now supports real class-scoped constants.Until relatively late in the evolution of ISO C++, there was no way to make a class-scoped constant whose value was known at compile time. A time-honored work-around has been to use an enumeration tag instead:

class C { enum { bufsize=1024 }; char buffer [bufsize]; // …

That is, the enumeration constant bufsize has nothing to do with enumerations, but is merely used to introduce the named constant. Modern compilers allow definition of class-scoped constants, so you should instead write:

class C { static const int bufsize= 1024; char buffer [bufsize]; // …

later, in an implementation file:const int C::bufsize; // don’t repeat the initializer

Specifying enum constant valuesSpecify enum constant values when it matters, leave them off when it doesn’t matter.

Enums representing bit-flagsTBA.

Use Readable Enums Rather Than Bool

C++ Programming Guidelines Page 55John M. Dlugosz 18-July-1999

Page 56: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Use Anonymous Namespaces for File-Local Things

Pointers vs. ReferencesFormatting Pointer/Reference Declarations

Material from this section adapted from The Guru’s Handbook.In C, it was common to position a * in a declaration next to the name, as this better reflects the grammar of declarations.

char *s; //1char c1, *s, c2, *s2; //2char *s = "hello"; //3

You see the simple form in line 1 above. This style facilitates putting multiple mixed declarations in one statement, as seen in line 2. However, it is bad for initializing in a declaration, as line 3 looks too much like an assignment to *s, and does not seem to be referring to just s.In C++, multiple declarations in one statement is frowned upon, and initializing is encouraged. The style used in Stroustrup's first C++ book was to put the * with the type. This is clear because the decoration is conceptually part of the type. It makes initialization clearer, and supporting multiple mixed declarations in one statement is not considered.

char* s= "hello world!";char* t= "hello to you to."; //same type, but don't combine.

Declare (and initialize) Variables Where NeededMaterial from this section adapted from The Guru’s Handbook.

One difference between C and C++ is that in C++ you may declare variables were needed, not just at the beginning of a block. This raises the question: should you? The answer is an emphatic yes.This ability was added to C++ because it is needed. Objects of class types have constructors. Constants and reference values also must be initialized. So, you can't define a variable until you are ready for it.For class types with a default constructor, letting it default and then assigning to it later is less efficient. For built-in types, it is possible to leave them uninitialized and assign to them later. But this is an exception to the general rule, and the philosophy of C++. You should make it a point to always initialize variables.

C++ Programming Guidelines Page 56John M. Dlugosz 18-July-1999

Page 57: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Avoid “Out” ParametersDon’t Use (void) Parameter ListDo not use (void) for a function taking no parameters. Always use an empty parameter list, ().

C++ Programming Guidelines Page 57John M. Dlugosz 18-July-1999

Page 58: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

OverloadingWhen functions do the same thing but operate on different types they should have the same name.

C++ Programming Guidelines Page 58John M. Dlugosz 18-July-1999

Page 59: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

C++ StatementsSwitch Statements

Return Statements

Goto Statements

C++ Programming Guidelines Page 59John M. Dlugosz 18-July-1999

Page 60: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

ClassesCanonical Object FormAny “normal”19 class should have considerations for the following four special member functions:

default constructor copy constructor copy assignment destructor

If the compiler is generating a default constructor, inspect the class to insure that this generated code is suitable. If it is, put a comment into the class, among the public member functions:

//default constructor generated by compiler.or

//C() generated by compiler.Or, if the generated code is found to be not suitable, and you do not intend to provide a useable default constructor, add a private member declaration to prevent the function from being generated, and comment it as such.

private:C(); //never defined

Note that the compiler only attempts to generate a default constructor if there are no constructors declared in the class.The same kind of treatment should always be applied to the copy constructor, destructor, and the copy assignment operator.If you don’t have one declared (so the compiler supplies it for you), add a comment where the declaration would go. This shows reviewers that you considered this member and know it’s OK, it means that people reading the header can stop looking for the function when they find the comment, and it gives a convenient place to add a real declaration if it should become necessary.In any case where the compiler-supplied function is not suitable, you must define your own, or declare it as private so it becomes unavailable (and comment it as such).Subsequent sections deal with details of these special members.

19 Other than “normal” classes, which are immune to this rule, include C-style “plain” structures and unions.

C++ Programming Guidelines Page 60John M. Dlugosz 18-July-1999

Page 61: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Rule 17. Classes must contain the 4 canonical members.

Relaxation:A simple class, in that the definition is short and it does not declare any data members, can omit the “compiler generated OK” comments if the class is used internally only and in a limited role, and can use a single “all compiler generated members OK” comment instead of several in the case where the simple class is public or prominent.

General Constructor IssuesRule 18. Constructors should be robust.

Watch for exceptionsIf an exception occurs during construction, make sure that you clean up any partially-constructed work in progress.Specifically,

make sure that the body of the constructor is exception safe; consider what happens if construction fails during base/member

initialization.Use member-init List, not Assignment in ConstructorsPrefer the base/member initialization list to assignment statements within the body of the constructor.Initialize all membersAll members should be initialized. If the member is a simple type without a default constructor, set it to a known state anyway. This is a trivial amount of overhead, and even if thought to be unnecessary, can make problems more repeatable.

General Destructor IssuesShould Destructors Be Virtual?Under the general guidelines of “robust” and “maintainable” with sufficient horsepower for the system, it doesn’t make sense to not make the destructor virtual simply to save a little space and speed up deletion a little.So, if a class has any virtual functions at all, the general rule is to make the destructor virtual too.

Rule 19. Destructors should generally be virtual.

C++ Programming Guidelines Page 61John M. Dlugosz 18-July-1999

Page 62: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Watch for ExceptionsA destructor should not allow exceptions to propagate out of them. Under normal conditions, it is harmless and works like any other function. But during stack unwinding (from another exception), it becomes a fatal error.So, you should “eat” any exceptions thrown during execution of the destructor. If you can detect whether stack unwinding is taking place (see uncaught_exception in the C++ Draft Working Paper, clause [18.6.4]) you could eat exceptions only during unwinding.To “eat” the exception, log it and exit from the catch handler without throwing or re-throwing anything.Meanwhile, this eating of exceptions can lead to suppression of error conditions. It should be possible to perform the “interesting” cleanup explicitly before letting a variable go out of scope, and the only thing left in the destructor is trivial and won’t encounter any more errors. See my article Two-Stage Destruction, reprinted on page 84, for a more comprehensive discussion.For example

//excerpt...try { file f (“foo.bar”); //...do stuff here... statement1(); statement2(); f.close(); }catch (std::exception& e)

Here, the object f has a destructor. The destructor will close the file automatically if necessary, so there is no need to call f.close() before going out of scope, as shown above.But, the destructor would “eat” the fact that an error occurred during closing of the file. Putting an explicit call to close() allows this error to be trapped and responded to by the code calling it.

Meanwhile, if statement1() threw an exception, f would be destructed during unwinding. Therefore, file::~file must take care of such details as closing the file itself, if needed. It’s there in case it’s needed, but doing it yourself gives better control over the process.

C++ Programming Guidelines Page 62John M. Dlugosz 18-July-1999

Page 63: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Rule 20. Exceptions may not escape from destructors.

Conversely, use such explicit shutdown control when available, rather than “just” letting objects go out of scope. Consider doing this when it’s meaningful to report such errors to the user, or if the program can react to them.Be PreparedThe destructor should be prepared to shut down the object at any time. Don’t require that the user leave the object in some “final” state before destroying it. Example: if a file is still open, close it.This is particularly an issue with objects that have asynchronous behavior. When the destructor is called, it should function correctly, even if that means dealing with clients of the object first.

Rule 21. A destructor may be called at any time.

Default Constructor IssuesSome uses of a class, such as making an array of them, require that a default constructor exist. Once this precedent exists, it’s not unreasonable to make this a requirement of other things such as containers and smart pointers.Scott Myers advises “Avoid gratuitous default constructors” as Item 4 in More Effective C++, but this limits the use of the class. If a default constructor does not make sense, or it would affect the implementation of the class (all members would need to check for “unknown”, for example), you can do without a default constructor. But if there is a state of the object (e.g. a closed file object) that is already supported that could make a reasonable default, provide a default constructor.

Copy Constructor Issueslet the compiler do itIt should only be necessary to write assignment operators for low-level classes that exist to encapsulate resources. Other classes containing such objects will know how to copy themselves.

prefix and postfix ++ and −−Assignment Operator Issues

revised 5-January-2001The standard copy-assignment operator looks something like this:

T& T::operator= (const T& other) { if (this == &other) return *this;

C++ Programming Guidelines Page 63John M. Dlugosz 18-July-1999

Page 64: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

//... do work here //... commit changes here return *this; }

T& or void return valueThe canonical form of the assignment operator is to return a reference to the object. Generally, this is so efficient that it’s not worth worrying about—the address of the object is already in a register for the purpose of accessing member data, and copying it to a different register for the return value is a trivial operation.But, an acceptable alternative form is to have a void return type.There is rarely a good reason to have any other return types.thread-safeThe assignment operator is just like any other mutator. Make sure it has the appropriate level of thread safety as specified in the class.let the compiler do itIt should only be necessary to write assignment operators for low-level classes that exist to encapsulate resources. Other classes containing such objects will know how to copy themselves.exception safeIf an exception propagates out of the assignment operator, it is unacceptable for the object to not be in a legal state (as with any other mutator). However, it is particularly important that an assignment operator behave in a “transacted” manner. If the assignment operator doesn’t work, the original value should be unchanged.

“other” copy functionsBesides the copy constructor and the copy assignment operator, many classes have additional functions for copying whole objects.Extra Copy ConstructorsA constructor whose first argument is a const T& can certainly have additional arguments as well. A constructor of this form should be thought of as a copy constructor with extra options.A constructor can be written to take a base class (say, const S& where T is derived from S) argument. This looks like a superset of the copy constructor, but the regular copy constructor still exists implicitly and will be used instead of the more general one. If you have a “super” constructor, don’t forget to define a (sadly redundant) copy constructor as well.

Rule 22. Extra constructors don’t remove the requirement for a copy constructor to be defined.

C++ Programming Guidelines Page 64John M. Dlugosz 18-July-1999

Page 65: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Extra Assignment FunctionsIt often makes sense for a class to have additional assignment operators besides the copy-assignment operator. That is, have additional operator= functions that take different argument types.These functions should be considered optimizations. If they were not provided, the class should perform in exactly the same way. That is,

string s;s= “hello”;

may call string::operator=(const char*), but if such a thing exists, it does exactly the same thing as string::operator=(string(“hello”)) would.Specifically, don’t have things that can be done via assignment that can’t be done via construction; and don’t have operator= do anything other than replace the complete value of an object.Sometimes you need an assignment function that takes extra arguments for special options or flags. Such functions should be named assign.Often there is a need for a “virtual copy constructor”, or a way to duplicate an object polymorphically. This function should be called clone and be written thus:

T* T::clone() const { return new T(*this); }Since the MSVC5 compiler doesn’t support overriding the return type of a virtual function, the return type would need to be defined a base pointer instead. It’s better to use the correct type, if the compiler supports it.

Use ‘explicit’ ConstructorsIf a constructor that takes one argument can’t be thought of as a conversion of representation, make it explicit.

complex c= 5;This makes sense; the value 5 is converted to a complex number, and it’s reasonable to think that the value of c is indeed five.

array a= 5;This does not make sense, but would be accepted if the class contained a constructor:

array::array (int size);The value of a is not five. This created an array of five elements, so the value is really {0,0,0,0,0}.By declaring the constructor as explicit, this mistake is avoided as it becomes a compile-time error. Also, in complex situations, it can remove ambiguities. In general, only have those implicit conversions available that you really want.

C++ Programming Guidelines Page 65John M. Dlugosz 18-July-1999

Page 66: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Use Objects To Wrap Resources… so dtor releases resource.

C++ Programming Guidelines Page 66John M. Dlugosz 18-July-1999

Page 67: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

C++ LibrariesOld vs. New Standard Headers

Updated 8-September-2000Use the new “standard” header files (that is, the dotless ones) rather than the older C or Microsoft-specific headers.In particular, use <iostream> instead of <iostream.h>.Also, note that standard C headers are available under new names for C++. Drop the .h and prefix a c. For example, C++ introduced <cstring> to replace <string.h>. However, the Microsoft compiler (as of Developer Studio 6) still implements them wrong: <cstring> is supposed to put everything into the std namespace, while <string.h> does not. Microsoft’s header doesn’t use the namespace correctly. So, don’t use the new c-prefixed forms of the ANSI C library headers, because they are incorrect.To summarize,

use <iostream> not <iostream.h>use <string.h> not <cstring>.

Use new/delete for Memory Managementdon’t use malloc/free instead. For other odd uses, encapsulate.Show standard way to get raw memory.Beware strdup().See also on page 68.

STL is Like Pointers

C++ Programming Guidelines Page 67John M. Dlugosz 18-July-1999

Page 68: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Memory ManagementEncapsulate Non-Standard Memory Management

Use Proper Vector/Scalar Form of Delete

Class-Specific New/Delete

Placement Arguments (Overloading New)

Errors in Operator NewNever check the return value; it throws an exception. Use nothrow if you need otherwise.

C++ Programming Guidelines Page 68John M. Dlugosz 18-July-1999

Page 69: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Threading IssuesThread-Specific DataThe Microsoft compiler supports the direct declaration of thread-specific data as a storage class, via __declspec(thread). Other compilers have similar things, but the restrictions vary.A serious restriction of this compiler enhancement is that it doesn’t support constructors and destructors. This makes it of limited use under C++.A more serious problem, which is OS related not a compiler issue, is that this storage class only works on code that’s linked with the image that starts the process. Translation: it doesn’t work in dynamically-loaded DLLs, since fixups are not applied.If you are producing DLLs for general reuse, you may not use the __declspec(thread) mechanism. For code that goes into an EXE file, or for DLLs that have a specific known use as part of a program, this restriction does not apply.As an alternative to __declspec(thread), use the DllTlsManager template in the types library of the DICOM Toolkit, or the thread-specific object mechanism in the Repertoire library.

Mutual ExclusionRegions of mutual exclusion, however controlled, must be exception safe. Using a mechanism that requires one call to enter the region and another call to exit the region is unacceptable.

void foo() //unacceptable style { EnterCriticalSection (cs); statement1(); statement2(); statement3(); LeaveCriticalSection (cs); }

If one of the statements in the locked region throws an exception, the call to LeaveCriticalSection is never made.Instead, use a locking object. The constructor locks the region, the destructor unlocks it. Use braces to precisely control the extent of the region, if necessary.

void foo() //OK { locker xx (cs); //constructor locks statement1();

C++ Programming Guidelines Page 69John M. Dlugosz 18-July-1999

Page 70: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

statement2(); statement3(); // destructor of xx automatically unlocks cs }

The DICOM Toolkit has a fairly abstract DTMutex that is controlled in this manner, and the Repertoire library has a rich set of mechanisms to choose from.

Static Local VariablesThe mechanism for static local variables (that is, constructing them the first time execution reaches that point) is not thread safe. So, any function that contains a static variable with a non-trivial initializer20 causes that function to be demoted to OTT. If you want higher thread safety, you have to assure that the function reaches that point at least once before you try multi-threading it, or you have to use mechanisms to ensure that the variable is initialized in a thread-safe manner.

20 A simple data type initialized with a constant expression (e.g. static int x=5;) will be loaded in with the program image, not computed at run time.

C++ Programming Guidelines Page 70John M. Dlugosz 18-July-1999

Page 71: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Disks and FilesDisk and File SizeAn install program tells me that there is –136912896 bytes available on drive E;. which is less than the 10 meg needed to install the program.The program doesn’t realize that a negative disk size is absurd.The moral: File and disk sizes don’t fit into 32-bit numbers. Even an unsigned long would not be correct, since disks greater than 4G in capacity are readily available.Solutions: Don’t use bytes for such values, but use K instead. Use 64-bit integers for such values.

File Name DelimitersFile names may contain spaces, so don’t make a space-separated list of file names!A more acceptable delimiter is the ‘\0’ character. The standard File Open dialog box, for example, returns multiple file names separated by ‘\0’ characters and an additional ‘\0’ to terminate the whole list.In general, assume that file names can contain all kinds of strange things, and always provide ways to quote the whole thing and/or escape out special characters within the name.The Win32 Platform SDK includes the following page:

Filename ConventionsAlthough each file system can have specific rules about the formation of individual components in a directory or filename, all file systems follow the same general conventions: a base filename and an optional extension, separated by a period. For example, the MS-DOS FAT file system supports 8 characters for the base filename and 3 characters for the extension. This is known as an 8.3 filename. The FAT file system and NTFS support filenames that can be up to 255 characters long. This is known as a long filename. To get an MS-DOS filename given a long filename, use the GetShortPathName function. To get the full path of a file, use the GetFullPathName function.

Both file systems use the backslash (\) character to separate directory names and the filename when forming a path.

General rules for applications creating names for directories and files or processing names supplied by the user include the following:

•Use any character in the current code page for a name, but do not use a path separator, a character in the range 0 through 31, or any character explicitly disallowed by the file system. A name can contain characters in the extended character set (128-255).

•Use the backslash (\), the forward slash (/), or both to separate components in a path. No other character is acceptable as a path separator.

C++ Programming Guidelines Page 71John M. Dlugosz 18-July-1999

Page 72: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

•Use a period (.) as a directory component in a path to represent the current directory.

•Use two consecutive periods (..) as a directory component in a path to represent the parent of the current directory.

•Use a period (.) to separate the base filename from the extension in a directory name or filename.

•Do not use the following characters in directory names or filenames, because they are reserved for Windows:

< > : " / \ |

•Do not use device names, such as aux, con, and prn, as filenames or directory names.

•Process a path as a null-terminated string. The maximum length for a path, including a trailing backslash, is given by MAX_PATH.

The wide (Unicode) versions of the CreateDirectory, FindFirstFile, GetFileAttributes, and SetFileAttributes functions permit paths that exceed the MAX_PATH length if the path has the "\\?\" or "\\?\UNC\" prefix. These prefixes direct the functions to turn off path parsing. Use the "\\?\" prefix with paths for local storage devices and the "\\?\UNC\" prefix with paths having the Universal Naming Convention format.

•Do not assume case sensitivity. Consider names such as OSCAR, Oscar, and oscar to be the same.

By following the rules listed in this section, an application can create valid names for files and directories regardless of the file system in use.

Recommendations:A maximal robust program should be prepared to accept any string as a file name.A common robust program should be able to correctly input and display filenames made up of characters from the “current” code page. The program may assume that character codes 1..31 are not used, nor are the characters <, >, “, :, /, \, or | (slashes separate parts). In particular, a common robust program may use “ as a delimiter without providing a way to escape out a “ contained within the filename.

File NamesA user should be able to specify a file name in any form that the operating system allows. This includes drive letters (e.g. e:\foo\bar.txt) and UNC names (e.g. \\machine\share\path\bar.txt).File names may be lengthy, may contain spaces or other strange characters, and may use either forward or backslashes.

C++ Programming Guidelines Page 72John M. Dlugosz 18-July-1999

Page 73: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

TemplatesPut Common Code Into a Non-Template Base Class

C++ Programming Guidelines Page 73John M. Dlugosz 18-July-1999

Page 74: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Miscellaneous ConventionsDatesDon’t write 2-9-98, since it’s ambiguous. With a global community of programmers, I frequently run into dates written dd/mm/yy which differs from my American mm/dd/yy format. I’m sure the other folks have the same complaint about our format.So, be international friendly by being unambiguous. At the very least, spell out the month. No matter what order the parts are written in, it is clear what is meant:

9-Sept-98 Sep 9, ’98 1998 September 9

While you’re at it, make sure the year is unambiguous too. With post-2000 dates, this value, if written as two digits, will also be in the domain of month numbers. So if you see “printed 1998 September 9” on the page and later come across “expiration date: 02 03 04”, it makes you wonder if that’s March 4th 2002 or March 3rd 2004.So, make sure that all three parts of a date are clearly indicative of which part they are. Years should use 4 digits or an apostrophe. Months should be spelled, not shown as numbers.An international standard for representing date and time values as text is ISO 8601:1988(E). It uses yyyymmdd ordering, with or without delimiters.Use the system’s Locale settings when printing date and time values, both for the preferred format and for the spellings of names.

Cause and Effect Should Be Close Together

ExpressivenessFunctions return results, and there is no complex mechanism required to check for errors. Temporary variables are not mandatory. There is no explicit cleanup required.Things could be written as one expression.

C++ Programming Guidelines Page 74John M. Dlugosz 18-July-1999

Page 75: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Windows 95 vs. NTWindows 95/98 is a different implementation from Windows NT/2000. This section contains issues that are caused by this difference.

ANSI or Unicode Version of APIMany Win32 functions are implemented as both -A and -W forms. Under Windows 95/98, the -A form is the native implementation and the -W form is supported on a select few functions, while the bulk of the -W functions return an error code 120 to indicate “not implemented”.Under Windows NT/2000, on the other hand, the -W forms are native, and the -A forms are wrappers that translate the strings and call the corresponding -W forms.If you always use the -A form as the least common denominator, you lose out on the enhanced functionality and instead have to face “code page” issues. There are also oddities concerning ANSI vs. OEM character sets to contend with when using the -A functions (see 43).In general, it’s best to use the -W (Unicode) versions when available. The recommended approach is to call the -W version if available, or the -A version otherwise, at run-time. Some C Run-Time Library functions are implemented this way21.

Functions Available only in NT or 95Sometimes, you want to call a function that is only available on one platform or the other. Instead of checking the OS version at run time to make your decision, check for the availability of that specific function. The reason is because service packs, updated system DLLs, and other upgrades can change the set of available functions. It is annoying to have a program state that it refuses to run under NT, for example, when you know perfectly well that the required features were added in a service pack.

Looking For StubsThe simplest thing to do is to actually call the function in question. If it is not implemented, the stub will return an error code 120. Then you know, regardless of what OS is in use, that this function is or is not available.Note that you should always test your stub-sensing code on a platform that doesn’t support the function. I’ve seen cases (such as the registry functions -W forms under Win95) where the stub doesn’t return the error code in the documented manner.

21 If you have the Microsoft CRT source installed, see AW_STR.C.

C++ Programming Guidelines Page 75John M. Dlugosz 18-July-1999

Page 76: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Avoiding Static LinkingSometimes, no stub is available. For example, when TryEnterCriticalSection was added in NT4, some versions of Win95 would not even load a program that called it. It didn’t matter that the code in question doesn’t call TryEnterCriticalSection if run on Win95; it refuses to load because it contains a call.This happens when a new API function is added, and established versions don’t know about it and don’t have a stub for it.When this happens, don’t code a (conditional) call to the function. Instead, use GetProcAddress. For example,

int __stdcall (*TryEnterCriticalSection) (HANDLE) =GetProcAddress (GetModuleHandle(“Kernel32.dll”), “TryEnterCriticalSection”);

//later…if (TryEnterCriticalSection) { //how to test for availability

y= TryEnterCriticalSection(x); //how to call}

C++ Programming Guidelines Page 76John M. Dlugosz 18-July-1999

Page 77: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Win32 GuidelinesFile Installation Locations

added 18-July-1999The default location offered by a setup program should be a subdirectory of the “Program Files” directory. The “Program Files” directory is named by ProgramFilesDir value in the registry key “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion”.Common files, such as a reusable DLL, should go in the “Common Files” directory, named by the CommonFilesDir value of the same key. Files that are named uniquely (such as specified in DLL Names on page 14) can go directly in this directory. Or, put files in a subdirectory of the “Common Files” location.Use the App Paths feature (see the registry key “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths”) to allow the application to find all its DLLs without polluting the system PATH environment variable.

Managing Common Filesadded 18-July-1999

Nomenclature Note: The registry is composed of keys, which are analogous to subdirectories on a file system, and values, which are analogous to the files. Keys contain values and other keys (often called subkeys to emphasize the relationship). Values, often called “named values” for clarity, hold a content much the way a file holds data. The value has both a name22 and a content, just as a file has both a name and a content.

Because keys and values are different kinds of objects, and they are easily confused in discussion, a this presentation consistently shows keys and values in different typefaces.

Common files (typically DLLs) that will be shared among multiple programs each of which can be installed or removed independently must be tracked for usage.There are two tracking mechanisms in use: The original one specified with Win32 uses a simple reference count in the SharedDLLs registry key. This was quickly shown to be lacking in robustness, so eventually Microsoft introduced a better way using the ModuleUsage registry key. Both of these keys are subkeys of “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion”.

Module UsageThe ModuleUsage registry key contains a subkey for each file to be tracked. However, because the backslash is a separator in registry keys just as it is for

22 Unlike files, the empty string is legal as a name. This “default entry” contributes to the confusion, as it (intentionally) appears that the key itself has data content.

C++ Programming Guidelines Page 77John M. Dlugosz 18-July-1999

Page 78: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

file paths, forward slashes are used. The name must be fully qualified and absolute. For example, “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ModuleUsage\D:/Common Files/Subdir/foobar.dll”23. This entry will have several named values describing its usage.Two named values of this key are .Owner and “.File Version” name the owner and (optionally) the installed version. Other named keys (which don’t begin with a dot) indicate clients. Note that the owner should register itself as a client, too.Since unregistering the last client (when that client is removed) can trigger removal of that shared component, it is not very clear which client “owns” the shared component. If this is the case for you, the value of .Owner may be set to “Unknown”24. Otherwise, the value of .Owner should be the identifier of one of the clients, as described below.All other named values indicate clients, one value per client. The value’s name needs to be unique. It could in fact be anything that’s guaranteed to conflict with anything else (e.g. a GUID), as long as the uninstaller that unregisters clients knows the same name that the installer used. However, a more meaningful name aids the human reader as well as “reaper” programs that try and eliminate unnecessary files and registry entries from your system.Specifically, the value name should either be a GUID (when the client is Internet Component Download) or a fully-qualified file name (for all other uses). So, use the file name of the main executable representing the client in question. If there is not a unique file that’s clearly the “main” one, or for an application suite installed as a single entity, use the directory name. The idea is that if the file (or directory) is not present, then the registered client is defunct and can be implicitly removed from the list.The contents of this named value can be anything you want. It could be information used by the uninstaller, readable notes, or left blank.For example,

\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ModuleUsage\D:/Common Files/Subdir/foobar.dll

[.Owner] = “D:\Program Files\Pseuper\Tree.exe”[.Version] = “1,2,3,4”[D:\Program Files\Pseuper\Tree.exe] = “”[D:\Program Files\HH\edit33.exe] = “installed 27-Oct-1999”

This DLL has two clients, one of which is the owner. Notice that the client names, being named values and not keys, use normal back-facing slashes in the file name. Only the key name uses forward-facing slashes.If you are explicitly installing a shared component, then set the owner to itself. If the component gets dragged in as a dependency as part of some 23 Note that the ‘/’ slashes are not delimiters, but are ordinary characters, and part of the rightmost subkey name.24 Microsoft documents the string “Unknown” in this regard. However, some components (e.g. MFC42.DLL on my machine) use “Unknown Owner” instead. An uninstaller or reaper should be prepared for some variation.

C++ Programming Guidelines Page 78John M. Dlugosz 18-July-1999

Page 79: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

other install, either put the latter as the owner (which you could argue gives special preference to the first thing installed that uses a common DLL) or leave it unowned.When uninstalling, note if any clients still exist, and warn the user before proceeding. Uninstalling something will remove client entries from other things, and when the last client is removed, that dependant thing, should be implicitly uninstalled too. If the client removed isn’t the last one but is the owner, it should be prompted for optional removal.

Shared DLLsEven though new programs should use the Module Usage mechanism described above, Microsoft states that the Shared DLLs value should be maintained as well, for backward compatibility.This may or may not be the case for new components. A new component documents its usage of ModuleUsage in its own install/uninstall code, and expects all programs incorporating that component to follow the identical mechanism. There should be no such thing as an “old” program, which knows only about the SharedDLLs counter, using this new component.But for completeness, here is how it works: The SharedDLLs key contains a named value for each common file. The named value is named for the fully-qualified filename of the file in question. Its content is a REG_DWORD containing the reference count. Uninstall programs decrement the reference count of all dependant components, physically deleting them if the count reached zero.

Consistent use of TCHARadded 7-August-2000

Microsoft provides an infrastructure for conditionally compiling C or C++ source code for either 8-bit or 16-bit characters. This centers around using a TCHAR type that conditionally compiles to either a char or wchar_t.However, if you use TCHARs, you must use them consistently. For example,

TCHAR string1 [80];TCHAR string2 [MAX_PATH];// …strcat (string2, string1);

The strcat function is for char arguments. If you compile this with UNICODE defined, it won’t work. That is missing the point of using TCHAR in the definitions. The code instead should use _tcscat, which will expand into strcat, wcscat, or _mbscat, as applicable.In general, don’t write code that assumes TCHAR is one way or the other. The whole point of TCHAR is that it could be either. The Win32 API functions are conditionally compiled to take the corresponding character type, and most of the string functions in the standard library have _tsc forms.A notable exception is printf. The _tprintf form changes the width of the first control string, but not of the %s substitutions. You’ll need your own

C++ Programming Guidelines Page 79John M. Dlugosz 18-July-1999

Page 80: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

conditional compilation to change %c and %s to %lc and %ls, when the arguments change from char to wchar_t.If you don’t plan on compiling the file in both UNICODE and non-UNICODE forms, don’t use TCHAR. If you include header files that do, avoid confusion by adding a guard like this:

#if defined UNICODE#error This file assumes TCHARs are 8-bit characters#endif

or#if !defined UNICODE#error This file assumes TCHARs are 16-bit characters#endif

If you use TCHAR, always test that your file compiles with and without UNICODE defined, even if you don’t link it or have immediate plans to use it the other way.A better way to write code is to always use Unicode internally, and deal with the presence or absence of –W forms of functions at run-time.

C++ Programming Guidelines Page 80John M. Dlugosz 18-July-1999

Page 81: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Win32 Faux Pas’sWhat is the correct plural of faux pas, anyway?

Tray Icons Should Watch for RestartsSometimes the Windows Explorer will reset itself. Besides redrawing all the desktop stuff, this has the annoying side effect of making some of your “tray” (a.k.a. Taskbar Notification Area) icons disappear.In order to avoid this faux pas, a program that registers an icon (via Shell_NotifyIcon) should also watch for TaskbarCreated messages. Microsoft’s documentation states:

Taskbar Creation NotificationMicrosoft® Internet Explorer 4.0 will notify applications that the taskbar has been created. When the taskbar is created, it will register a message with the "TaskbarCreated" string and then broadcast this message to all top-level windows. When your taskbar application receives this message, it should assume that any taskbar icons it added have been removed and add them again. The following example shows a very simplified method for handling this case.

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam){static UINT s_uTaskbarRestart;

switch(uMessage) { case WM_CREATE: s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); break; default: if(uMessage == s_uTaskbarRestart) AddTaskbarIcons(); break; }return DefWindowProc(hWnd, uMessage, wParam, lParam);}

Computers May Have Multiple DisplaysAdded 26-August-2001

A logical “desktop” may span across several monitors. This is common for developers, and users may take more advantage of it as they upgrade their monitors: the old small monitor can become the second. Some video cards support multiple monitors directly; or multiple PCI cards can be installed. Again, when upgrading the video card, why not keep the old one too, if there is a spare slot?Programs that check for screen boundaries in order to reposition windows need to understand this. Too often, programs think they’re doing me a favor

C++ Programming Guidelines Page 81John M. Dlugosz 18-July-1999

Page 82: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

when they force windows to appear in the primary screen, ignoring the desired location on a secondary screen.This includes checking the saved window position when re-creating a window, to see if it’s still a valid location; and checking values after computing coordinates to make something line up relative to an existing window.When doing this, before moving a window “back on screen”, first find out which screen it’s already part of. See the Win32 API functions MonitorFromPoint, MonitorFromRect, and MonitorFromWindow. Or, to find out what’s available, call EnumDisplayMonitors.Then, instead calling SystemParametersInfo for SPI_GETWORKAREA (which always returns the working area of the primary monitor), call GetMonitorInfo instead.See also “How to Exploit Multiple Monitor Support in Memphis and Windows NT 5.0” from the June 1997 issue of Microsoft Systems Journal. This can be found in the Microsoft TechNet CD or on the web.

C++ Programming Guidelines Page 82John M. Dlugosz 18-July-1999

Page 83: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Appendix A: Reprinted PapersThis appendix contains reprints of materials referred to in the main document.

C++ Programming Guidelines Page 83John M. Dlugosz 18-July-1999

Page 84: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Reprinted courtesy of the Cobb Group. This article is part of a series that appeared in Inside Microsoft C++ and Borland C++ Developer’s Journal.

C++ CODING PRACTICES

Two-stage Destructorsby John M. DlugoszThe MFC 4.0 online book that comes with Visual C++ reads, "Two-stage construction is always safer. In one-stage construction, the constructor could throw an exception if you provide incorrect arguments or memory allocation fails. That problem is avoided by two-stage construction, although you do have to check for failure."

// Normal: constructor throws exceptionCPen myPen1( PS_DOT, 5, RGB(0,0,0) ); // One-stage

// Two-stage: first construct the penCPen myPen2; //default constructor never errors// Then set up your desired stateif( myPen2.CreatePen( PS_DOT, 5, RGB(0,0,0) ) ) { // Use the pen

Whoever wrote this is missing the point. If you let the constructor throw exceptions, error handling is forced upon you, without special error checking needed after each function call. With the "two stage" construction method, you have to remember to check for error codes, and you lose the benefit of exceptions.It is fitting and proper to have constructors throw exceptions. On the other hand, a serious topic that is often overlooked is handling errors in destructors.The Great Cleanup ParadoxA destructor is used to perform necessary cleanup operations, and can therefore perform some non-trivial operations. For example, an object that represents a file (sketched in Figure A) will have a destructor that closes the file. This entails flushing the buffer, closing the file, and freeing the buffer. What if the write operation required for the flush encounters a disk error? In other words, destructors do work which may involve errors. You would therefore suppose that destructors can throw exceptions just like any other function. This is in fact allowed—when an object goes out of scope under normal circumstances, if the destructor throws an exception the EH mechanism is invoked just as with any other function call.However, destructors are also invoked under abnormal circumstances. When the stack is being unwound due to throwing an exception, destructors are invoked. Throwing another exception out of one of those destructors is a fatal error. Don't misunderstand—during stack unwinding, you can still throw and catch exceptions. You just can't let an exception propagate out of the destructor that was called by the stack-unwinding mechanism.

C++ Programming Guidelines Page 84John M. Dlugosz 18-July-1999

Page 85: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

So, you have a situation where exceptions seem warranted for destructors, yet exceptions from destructors cause problems. Even when working correctly, experience shows that handling such exceptions can be a little confusing.Repressing the Perils of DestructingIt seems that the only solution is make sure that destructors don't allow exceptions to propagate out. Then, we must deal with the fact that error information is repressed.For a destructor to "keep it all inside", it can use a try block around the whole thing. For example:

file::~file () throw() { try { close(); delete[] buffer; } catch (...) { // eat it. }}

Now, any exceptions thrown by close() are repressed, and the destructor gives no indication that there was a problem. The throw() in the signature adds enforcement and documentation to indicate that no exception will ever propagate out of this function. This is explained in more detail in the article on Exception Specifications.SummaryFor standard design procedure, it is recommended that you prevent exceptions from ever propagating out of destructors. For any non-trivial destructor, use a try block around the whole body, and optionally an exception specification. Meanwhile, provide a way to trigger the shut-down process explicitly, which throws exceptions normally. After the shut down process, destruction should be trivial.This provides automatic cleanup when required or desired, yet provides a place to put error handling code.

Listing A: A hypothetical file class.class file {public: file (); //construct closed file (string filename); void open (string filename); void close(); size_t read (void* buf, size_t); size_t write (const void* buf, size_t); };

C++ Programming Guidelines Page 85John M. Dlugosz 18-July-1999

Page 86: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Appendix B: BibliographyDlugosz, John M.http://www.dlugosz.com, [email protected]

Pietrek, Matthttp://www.tiac.com/users/mpietrek/, [email protected]

C++ Programming Guidelines Page 86John M. Dlugosz 18-July-1999

Page 87: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Appendix C: ReferencesThis appendix lists the materials referred to in the main document that are not reprinted in Appendix A.CD2 ANSI C++ Committee Draft 2The Guru's Handbook

C++ Programming Guidelines Page 87John M. Dlugosz 18-July-1999

Page 88: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Appendix D: ListingsBecause of the need to re-test, or examine exactly how something was tested or demonstrated, source code is included here.

CheckOEMCheckOEM.cpp

#define WIN32_LEAN_AND_MEAN#include <WINDOWS.H>#include <stdio.h>

int main () { BOOL x= AreFileApisANSI(); printf ("The result of AreFileApisANSI() is %d\n", x); return 0; }

C++ Programming Guidelines Page 88John M. Dlugosz 18-July-1999

Page 89: Title Goes Here - Dlugosz  · Web viewSemantically, according to Microsoft's documentation for VC++5.0 the __declspec modifier can appear anywhere within the decl-specifierseq, with

Indexbool, 57casting, 46comments

formatting, 18useless, 17

const, 56constructor

static, 53date

format, 76enum, 57file names, 73, 74file size, 73global names

friendly, 15unique, 13

header, 8–12include files

standard, 69macros, 46

memory management, 70message

TaskbarCreated, 84mutual exclusion, 71namespace

in header, 9pointer

vs. reference, 58pragma once, 9reference

vs. pointer, 58Shell_NotifyIcon, 84taskbar notification area, 84thread-specific data, 71tray. see taskbar notification areausing

in header, 10void

in parameter list, 59

C++ Programming Guidelines Page 89John M. Dlugosz 18-July-1999