PrintNumber ErrorLocation Error Correction DateAdded
1 p iv Pearson Education, Inc.
Rights and Contracts Department
75 Arlington Street, Suite 300
Boston, MA 02116
Fax: (617) 848-7047
ISBN-10: 0-321-22877-4
Text printed on recycled paper
12345678910—MA—0605040302
First printing, August 2004
Pearson Education, Inc.
Rights and Contracts Department
501 Boylston Street, Suite 900
Boston, MA 02116
Fax: (617) 671-3447
ISBN-10: 0-321-22877-4
ISBN-13: 978-0-321-22877-2
Text printed on recycled paper
Second printing, October 2009
9/18/2009
1 p xix Thanks to my loyal band of reviewers—Chuck Allison, Dave Brooks, Darren Lynch, Duane Yates, Eugene Gershnik, Gary Pennington, George Frasier, Greg Peet, John Torjo, Scott Patterson, and Walter Bright—without whom I’d have a flat face from having fallen on it too many times. Thanks to my loyal band of reviewers—Chuck Allison, Dave Brooks, Darren Lynch, Duane Yates, Eugene Gershnik, Gary Pennington, George Frazier, Greg Peet, John Torjo, Scott Patterson, and Walter Bright—without whom I’d have a flat face from having fallen on it too many times. 9/18/2009
1 p xx And finally, thanks to all the STLSoft users, without whose feedback many of the features of the library would not be, and parts of this book would have been all the harder to research and to write. And finally, thanks to all the STLSoft users, without whose feedback many of the features of the library would not be, and parts of this book would have been all the harder to research and to write.
I’d also like to thank in the readers of the first printing who sent in errata along with their mostly positive feedback: Dirk Bonekaemper, Ike Naar, James Widman, Jon Redinger, Jonathan Small, Jonny Rodin, Juan Antonio Zaratiegui Vallecillo, Mark Hoebeke, Markus Elfring, Martin Boucher, Martin Moene, Martin Rottinger, Michael Toksvig, Pablo Aguilar, Paul Floyd, Phil Hodges, Rajanikanth Jammalamadaka, Robert Dexter, Rupert Kittinger, Sam Saariste, Serge Krynine, Sergey Racheyev, Steven Allan Tague, Thomas Schell, Will Trobaugh and, last, but by no means least, my friend Scott Meyers, a much more talented writer than I.
9/18/2009
1 p xxv • Uses Design-by-Contract (see section 1.3) wherever compile-time error-detection is not possible • Uses Design-by-Contract (see section 1.3) wherever compile-time error-detection is not appropriate 9/18/2009
1 p xxv Generality. I’ve never properly understood the word genericity, at least in so far as a programming context, albeit that I sometimes bandy about the term with alacrity. I guess it means being able to write template code that will work with a variety of types, where those types are related by how they are used rather than how they are defined, and I restrict its use to that context. Generality [Kern1999] seems both to mean it better, and to apply the concept in a much broader sense: I’m just as interested in my code working with other headers and other libraries than merely with other (template parameterizing) types. Generality. I’ve never properly understood the word genericity, at least in so far as a programming context, albeit that I sometimes bandy about the term with alacrity. I guess it means being able to write template code that will work with a variety of types, where those types are related by how they are used rather than how they are defined, and I restrict its use to that context. Generality [Kern1999] seems both to mean it better, and to apply the concept in a much broader sense: I’m just as interested in my code working with other headers and other libraries than merely with other (template instantiating) types. 9/18/2009
1 p xxvi I tend not to use the term compound types, since I think the term implies things that are made up of other things, which can’t really be said for references and pointers. I tend not to use the term compound types, since I think the term implies things that are made up of several other things, which can’t really be said for references and pointers. 9/18/2009
1 p xxix Instances can be (in)equality compared with any other instances, and even with themselves. Equality (and inequality) is reflexive, symmetric, and transitive. Instances can be (in)equality compared with any other instances, and even with themselves. Equality is reflexive, symmetric, and transitive. 9/18/2009
1 p xxxiii Imperfection: C++ provides no type-safety for the use of conceptual defined types, except where such types have incompatible underlying types.  (p. 294) Imperfection: C++ provides no type-safety for the use of conceptually-defined types, except where such types have incompatible underlying types.  (p. 294) 9/18/2009
1 p xxxiii The values returned from attribute shims are always valid outside the instance of the shim, in cases where the shim is implemented by the creation of a temporary object.
Definition: Logical Shims  (p. 346)
Logical shims are a refinement of attribute shims in that they report on the state of an instance to which they are applied.
The names of logical shims take the form of an interrogative coupled with the particular attribute or state being queried. Examples would be is_open, has_element, is_null.
The values returned from attribute shims are always valid outside the lifetime of the shim, even in cases where the shim is implemented by the creation of a temporary
object.
Definition: Logical Shims  (p. 346)
Logical shims are a refinement of attribute shims in that they report on the state of an instance to which they are applied.
The names of logical shims take the form of an interrogative element coupled with the particular attribute or state being queried. Examples would be is_open, has_element, is_null.
9/18/2009
1 p 1 In a cycling road race, the first hour or so of racing is generally pretty steady, with all the riders feeling their way into the race, warming up overtrained muscles, taking on board a last few calories, planning their strategies and tactics for the coming hours, and renewing acquaintances with friends and colleagues who they may not have seen for some time. In a cycling road race, the first hour or so of racing is generally pretty steady, with all the riders feeling their way into the race, warming up overtrained muscles, taking on board a last few calories, planning their strategies and tactics for the coming hours, and renewing acquaintances with friends and colleagues whom they may not have seen for some time. 9/18/2009
1 p 4 • It’s better to catch a bug during unit testing than during debug system testing.
• It’s better to catch a bug during debug system than in prerelease/beta system testing.
• It’s better to catch a bug during unit testing than during debug system testing.
• It’s better to catch a bug during debug system testing than in prerelease/beta system testing.
9/18/2009
1 p 7 (This may make Borland’s perplexing error message for must_be_subscriptable seem that little bit less nonsensical, but that’s going to be of much help to us in tracking down and understanding the constraint violation.) (This may make Borland’s perplexing error message for must_be_subscriptable seem that little bit less nonsensical, but that’s going to be of little help to us in tracking down and understanding the constraint violation.) 9/18/2009
1 p 7 It is axiomatic that anything subscriptable by offset[pointer] will also be subscriptable by pointer[offset], so there’s no need to incorporate must_be_subscriptable within must_be_subscriptable_as_decayable_pointer. Where the constraints have different ramification, though, it can be appropriate to use inheritance to bring two constraints together. It is axiomatic that anything subscriptable by offset[pointer] will also be subscriptable by pointer[offset], so there’s no need to incorporate must_be_subscriptable within must_be_subscriptable_as_decayable_pointer. Where the constraints have different ramifications, though, it can be appropriate to use inheritance to bring two constraints together. 9/18/2009
1 p 8 struct must_be_pod
{
. . .
static void constraints()
{
union
{
T T_is_not_POD_type;
};
}
. . .
template <typename T>
struct must_be_pod
{
. . .
static void constraints()
{
union
{
T T_is_not_POD_type;
};
}
. . .
9/18/2009
1 p 11 The last reason is eminently practical. Different compilers do slightly different things with the constraints, which can require a lot of jiggery-pokery. For example, depending on the compiler, the constraint_must_be_pod() is defined in one of three forms: The last reason is eminently practical. Different compilers do slightly different things with the constraints, which can require a lot of jiggery-pokery. For example, depending on the compiler, constraint_must_be_pod() is defined in one of three forms: 9/18/2009
1 p 11 One of my reviewers commented that some of these constraints could have been done via template meta-programming (TMP) techniques,5 and he’s quite correct. For example, the must_be_pointer constraint could be implemented as a static assertion (see section 1.4.7) coupled with the is_pointer_type trait template used in section 33.3.2, as follows: One of my reviewers commented that some of these constraints could have been done via template meta-programming (TMP) techniques,5 and he’s quite correct. For example, the must_be_pointer constraint could be implemented as a static assertion (see section 1.4.8) coupled with the is_pointer_type trait template used in section 33.3.2, as follows: 9/18/2009
1 p 11 4For the reasons described in section 12.4.4, it’s always a good reason to name macros in uppercase. The reason I didn’t do so with the constraint macros is that I wanted to keep the case the same as the constraint types. In hindsight it seems less compelling, and you’d probably want to use uppercase for your own. 4For the reasons described in section 1.4.5, it’s always a good reason to name macros in uppercase. The reason I didn’t do so with the constraint macros is that I wanted to keep the case the same as the constraint types. In hindsight it seems less compelling, and you’d probably want to use uppercase for your own. 9/18/2009
1 p 14 Precondition testing is easily done in C++. We’ve seen several examples of this already in the book. It’s as simple as using an assertion: Precondition testing is easily done in C++. It’s as simple as using an assertion: 9/18/2009
1 p 15 I’m not even going to start suggesting how the three individual monitor objects could be combined in such a way as to enforce a wide variety of postcondition states; such things might be an exciting challenge for a template meta-programmer doing postgraduate research, but for the rest of us we’re way past the level at which the complexity is worth the payoff. I’m not even going to start suggesting how the three individual monitor objects could be combined in such a way as to enforce a wide variety of postcondition states; such things might be an exciting challenge for postgraduate research, but for the rest of us we’re way past the level at which the complexity is worth the payoff. 9/18/2009
1 p 18 Basically, an assertion is a runtime test that is usually only conducted in debug or testing builds, and tends to take the following form: A runtime assertion is a test that is usually only conducted in debug or testing builds, and tends to take the following form: 9/18/2009
1 p 18 7In ISE Eiffel 4.5 you cannot remove preconditions; presumably the rationale is that preconditions can react before program state becomes undefined and, therefore, it makes sense to continue the program by catching the violation exception. 7In ISE Eiffel 4.5 you cannot remove preconditions; the rationale is that preconditions are generally cheap enough to not ruin runtime performance. 9/18/2009
1 p 19 #ifdef NDEBUG
# define assert(x) ((void)(0))
#elif /* ? NDEBUG */
extern "C" void assert_function( char const *expression
, char const *file
, int line);
# define assert(x) ((!x) ?
? assert_function(#x, _ _FILE_ _, _ _LINE_ _)
: ((void)0))
#endif /* NDEBUG */
Since the expression in an assertion is elided from release builds, it is very important that the expression have no side effects. Failure to adhere to this will lead to the curious and vexing situation whereby your debug builds work and your release builds do not.
#ifdef NDEBUG
# define assert(x) ((void)(0))
#elif /* ? NDEBUG */
extern "C" void assert_function( char const *expression
, char const *file
, int line);
# define assert(x) ((!x)
? assert_function(#x, _ _FILE_ _, _ _LINE_ _)
: ((void)0))
#endif /* NDEBUG */
Since the expression in an assertion is elided from release builds, it is very important that the expression have no side effects. Failure to adhere to this may lead to the curious and vexing situation whereby your debug builds work and your release builds do not.
9/18/2009
1 p 21 I really can’t understand this one. Given that just about everybody develops software on desktop hardware, with virtual memory systems, the only way you’re ever likely actually to experience a memory system during debugging is when you’ve plugged in a low-stock allocation mechanism or stipulated low-stock behavior to your runtime library’s debugging APIs. I really can’t understand this one. Given that just about everybody develops software on desktop hardware, with virtual memory systems, the only way you’re ever likely actually to experience a memory exception during debugging is when you’ve plugged in a low-stock allocation mechanism or stipulated low-stock behavior to your runtime library’s debugging APIs. 9/18/2009
1 p 24 Although this is a Win32 + Intel architecture specific point, it’s worth noting because it’s very useful and surprisingly little known. The Win32 API function DebugBreak() causes the execution of the calling process to fault with a breakpoint exception. Although this is a Windows + Intel architecture specific point, it’s worth noting because it’s very useful and surprisingly little known. The Windows API function DebugBreak() causes the execution of the calling process to fault with a breakpoint exception. 9/18/2009
1 p 25 So far we’ve just looked at runtime assertions. But catching bugs at runtime is a poor second best to catching them at compile time. In many parts of the book we’ve mentioned static assertions, also called compile-time assertions, so now’s a good time to look at them in detail. So far we’ve just looked at runtime assertions. But catching bugs at runtime is a poor second best to catching them at compile time. In many parts of the book are mentioned static assertions, also called compile-time assertions, so now’s a good time to look at them in detail. 9/18/2009
1 p 27 In an attempt to ameliorate this confusion, Andrei Alexandrescu, in [Alex2001], describes a technique for providing better error messages, and gets as far as is probably possible within the current limitations of the language.11
For my part, I tend to shy away from that level of complexity for three reasons. First, I’m lazy, and like to avoid complexity where possible.12 Second, I write a lot of C code as well as C++, and I prefer wherever possible to have the same facilities available to me with both languages.
In an attempt to ameliorate this confusion, Andrei Alexandrescu, in [Alex2001], describes a technique for providing better error messages, and gets as far as is probably possible within the current limitations of the language.
For my part, I tend to shy away from that level of complexity for three reasons. First, I’m lazy, and like to avoid complexity where possible.11 Second, I write a lot of C code as well as C++, and I prefer wherever possible to have the same facilities available to me with both languages.
9/18/2009
1 p 27 11 You should check it out. It’s quite nifty.
12 I also think it’s good to have your supporting techniques as simple as possible, although I admit that there have been sojourns to the land of a trillion brain cells in several parts of the book, so I can’t seriously claim that as a good reason in this case. It’s just laziness.
11 I also think it’s good to have your supporting techniques as simple as possible, although I admit that there have been sojourns to the land of a trillion brain cells in several parts of the book, so I can’t seriously claim that as a good reason in this case. It’s just laziness. 9/18/2009
1 p 30 for example, those that contain pointers to allocated resources—the default member-wise copy of a compiler provided copy constructor can result in two class instances thinking that they own the same resource. This does not end happily. for example, those that contain pointers to allocated resources—the default member-wise copy of a compiler-provided copy constructor can result in two class instances thinking that they own the same resource. This does not end happily. 9/18/2009
1 p 32 By hiding the destructor we forcibly prevent the use of frame/global variables, and we also prevent delete being applied to an instance to which we have a pointer. This can be of use when we have access to an object belonging to something else that we can use but not destroy. It is especially useful in preventing misuse of reference-counted pointers. By hiding the destructor we forcibly prevent the use of stack/global variables, and we also prevent delete being applied to an instance to which we have a pointer. This can be of use when we have access to an object belonging to something else that we can use but not destroy. It is especially useful in preventing misuse of reference-counted pointers. 9/18/2009
1 p 33 Naturally, this also does not work if you don’t actually create any instances of the type, and only call its static members. But in almost all cases you have your bases covered by exercising constraints in the destructor. Naturally, this also does not work if you don’t actually create any instances of the type, and only call its static members. But in most cases you have your bases covered by exercising constraints in the destructor. 9/18/2009
1 p 34 Of these seven, only the last, array member variables, cannot be initialized in a member initializer list (or MIL), and the first five must be. Normal, non-const, nonreference, scalar member variables can be “initialized” within either the initializer list or within a constructor body. In fact, they undergo assignment, rather than initialization in this case. Of these seven, only the last, array member variables, cannot be initialized in a member initializer list (or MIL), and the first five must be. Normal, non-const, nonreference, scalar member variables can be “initialized” within either the initializer list or within a constructor body. In fact, they undergo assignment, rather than initialization in the latter case. 9/18/2009
1 p 35 That took all of 10 minutes, and the class saved itself from a significant amount of accreted waste, not to mention a couple of errors. That took all of 10 minutes, and the class saved itself from a significant amount of accreted waste, not to mention a couple of defects. 9/18/2009
1 p 37 The String instance may be constructed from either a pointer to a string, or a literal empty string. (As we will see in section 15.4.3, literal constants may or may not be folded—identical literals are merged into one by the linker—so this implementation is partial, and a fuller one would have to address this potential problem.) The String instance may be constructed from either a pointer to a string, or a literal empty string. (As we will see in section 15.4.3, literal constants may or may not be folded—where identical literals are merged into one by the linker—so this implementation is partial, and a fuller one would have to address this potential problem.) 9/18/2009
1 p 38 Despite the seemingly innocuous looking initializer list, instances of Fatal will have arbitrary garbage in their x members, because at the time x is initialized y has not been. You should avoid such dependencies as a rule. Only GCC detects this and issues a warning (when the
-Wall option is used).
Despite the seemingly innocuous looking initializer list, instances of Fatal will have arbitrary garbage in their x members, because at the time x is initialized y has not been. You should avoid such dependencies as a rule. GCC detects this and issues a warning (when the
-Wall option is used).
9/18/2009
1 p 38 Alas, this particular class is not the best example of when it is appropriate to thumb our nose at the law, because it is achieves const member variables by using the offsetof macro, which is itself, in this circumstance, nonstandard, as we discuss in section 2.3.3. Alas, this particular class is not the best example of when it is appropriate to thumb our nose at the law, because it achieves const member variables by using the offsetof macro, which is itself, in this circumstance, nonstandard, as we discuss in section 2.3.3. 9/18/2009
1 p 41 Service service;

. . . // Initialize the service, and make some connections

pthread_mutex_lock(service.lock);
int i = recv( service.rxChannel, service.rxBuffer
, service.rxBufferSize);
. . . // Many more lines of low-level coding
pthread_mutex_lock(&service.unlock);
Service service;

. . . // Initialize the service, and make some connections

pthread_mutex_lock(service.lock);
int i = recv( service.rxChannel, service.rxBuffer
, service.rxBufferSize);
. . . // Many more lines of low-level coding
pthread_mutex_unlock(&service.lock);
9/18/2009
1 p 44 tokLength = 1 + (context->currTokEnd –context->currTokBegin);
tokName = strncpy( (char*)(*context->pfnAlloc)(sizeof(char)
* tokLength), context->currTokBegin, tokLength);
size_t cchDest = 0;
tokName = strncpy( (char*)(*context->pfnAlloc)(sizeof(char)
* tokLength), context->currTokBegin, tokLength);
cchDest = 0;
9/18/2009
1 p 44 Not pretty is it? As well as being a royal pain to deal with all that code, we’re messing around with raw pointers in our logic code, and the handling of errors and exceptions is sketchy at best. All these things are what C++ positively excels at, so surely we can improve it. Not pretty is it? As well as being a royal pain to deal with all that code, we’re messing around with raw pointers in our logic code, and the handling of errors and exceptions is sketchy at best. All these things are what C++ positively excels at, so we can surely improve it. 9/18/2009
1 p 47 {
DeviceCloser dc;
int *&device = dc.m_file;
{
DeviceCloser dc;
int &device = dc.m_device;
9/18/2009
1 p 47 Naturally, it’s preferable to initialize the resource handler in its constructor, but this is not always possible, or convenient at least. Consider the case where we might need to perform some other actions on the conditional branch where we open the device from a socket. These other actions might cause an exception to be thrown. One option is to move all that branch’s code out into a separate function which itself will either return the opened socket, or will catch the thrown exception, close the socked handle, and rethrow the exception. But that’s not necessarily always preferable. Naturally, it’s preferable to initialize the resource handler in its constructor, but this is not always possible, or convenient. Consider the case where we might need to perform some other actions on the conditional branch where we open the device from a socket. These other actions might cause an exception to be thrown. One option is to move all that branch’s code out into a separate function which itself will either return the opened socket, or will catch the thrown exception, close the socket handle, and rethrow the exception. But that’s not necessarily always preferable. 9/18/2009
1 p 48 4The Boost shared_ptr can be used to provide nonintrusive reference-counting. 4Boost and STLSoft provide shared_ptr smart pointer class templates that can be used to provide nonintrusive reference-counting. 9/18/2009
1 p 50 Now the container automatically does what you would want in the destructor, saving you from the manual and error prone boilerplate. We’ve been able to leverage the automatic destruction support provided by C++ of the parameterized sequence_container_veneer class to effect desired behavior, rather than having to create a specific class to do our task.
It is important to note that the destruction of the outstanding Resource instances is as a result of parameterizing with the RelResource type, rather than an artifact of the sequence_container_veneer class per se.
Now the container automatically does what you would want in the destructor, saving you from the manual and error prone boilerplate. We’ve been able to leverage the automatic destruction support provided by C++ of the specialized sequence_container_veneer class to effect desired behavior, rather than having to create a specific class to do our task.
It is important to note that the destruction of the outstanding Resource instances is as a result of specializing with the RelResource type, rather than an artifact of the sequence_container_veneer class per se.
9/18/2009
1 p 51 It is this mechanism that allows objects to clear up after themselves—actually it’s the compiler, the author of the class and, sometimes, the machine itself who do it—rather than you having to do it. This is the essence of the power of C++’s RAII support. However the object comes to be destroyed, its destructor will be called. It is this mechanism that allows objects to clean up after themselves—actually it’s the compiler, the author of the class and, sometimes, the machine itself who do it—rather than you having to do it. This is the essence of the power of C++’s RAII support. However the object comes to be destroyed, its destructor will be called. 9/18/2009
1 p 55 The breakdown of data encapsulation is reflected in C++’s access specifiers. Unencapsulated data, which is defined in a public section of a class (or the default section of a struct or union), is accessible to any other context. (Fully) encapsulated data is defined in a private or protected section of a class type. The breakdown of data encapsulation is reflected in C++’s access specifiers. Unencapsulated data, which is defined in a public section of a class (or the default section of a struct or union), is accessible to any other context. (Fully) encapsulated data is defined in a private section of a class type. 9/18/2009
1 p 57 str1 = str2 + " " + str3; // 1
if(!str3) { . . . } // 2
str2.Empty() // 3
++str; // 4
str1 = str2 + " " + str3; // 1
if(!str3) { . . . } // 2
str2.Empty(); // 3
++str; // 4
9/18/2009
1 p 57 4In Perl, and some other scripting languages, a string may be increment by making a best interpretation of it as a numeric type, then incrementing that value, and then converting back to a string. That’s fine for Perl, but I don’t think that’s a good thing for C++ code to be doing. 4In Perl, and some other scripting languages, a string may be incremented by making a best interpretation of it as a numeric type, then incrementing that value, and then converting back to a string. That’s fine for Perl, but I don’t think that’s a good thing for C++ code to be doing. 9/18/2009
1 p 60 Types may also provide additional methods so that client code will not have to hand-code common or expected operations. Unfortunately, this can be an open set of possibilities, even when we are being good citizens and maintaining orthogonality. This can be ameliorated by preferring free functions to class methods wherever possible [Meye2000].
Hence, our classified implementation might look like the class UInteger64 (see Listing 4.1), which aggregates a uinteger64 as a member variable (so that it may reuse the UI64_*() API).
Listing 4.1
class UInteger64
{
public:
UInteger64();
UInteger64(uint32_t low);
UInteger64(uint32_t high, uint32_t low);
#ifdef ACMELIB_COMPILER_SUPPORTS_64BIT_INT
UInteger64(uint64_t low);
#endif /* ACMELIB_COMPILER_SUPPORTS_64BIT_INT */
UInteger64(UInteger64 const &rhs);
Types may also provide additional methods so that client code will not have to hand-code common or expected operations. Unfortunately, this can be an open set of possibilities, even when we are being good citizens and maintaining orthogonality. This can be ameliorated by preferring free functions to class methods wherever possible [Meye2000], as we’ll see in the next section.
Hence, our classified implementation might look like the class UInteger64 (see Listing 4.1), which aggregates a uinteger64 as a member variable (so that it may reuse the UI64_*() API).
Listing 4.1
class UInteger64
{
public: /// Construction
UInteger64();
UInteger64(uint32_t low);
UInteger64(uint32_t high, uint32_t low);
#ifdef ACMELIB_COMPILER_SUPPORTS_64BIT_INT
UInteger64(uint64_t low);
#endif /* ACMELIB_COMPILER_SUPPORTS_64BIT_INT */
UInteger64(UInteger64 const &rhs);
9/18/2009
1 p 61 // Comparison
public:
static bool IsLessThan(UInteger64 const &i1, UInteger64 const &i2);
static bool IsEqual(UInteger64 const &i1, UInteger64 const &i2);
static bool IsGreaterThan(UInteger64 const &i1, UInteger64 const &i2);

// Arithmetic operations
public:
static UInteger64 Multiply(UInteger64 const &i1, UInteger64 const &i2);
static UInteger64 Divide(UInteger64 const &i1, UInteger64 const &i2);

private:
uinteger64 m_value;
};
public: /// Comparison
static bool IsLessThan(UInteger64 const &i1, UInteger64 const &i2);
static bool IsEqual(UInteger64 const &i1, UInteger64 const &i2);
static bool IsGreaterThan(UInteger64 const &i1, UInteger64 const &i2);

public: /// Arithmetic operations
static UInteger64 Multiply(UInteger64 const &i1, UInteger64 const &i2);
static UInteger64 Divide(UInteger64 const &i1, UInteger64 const &i2);

private: /// Member Variables
uinteger64 m_value;
};
UInteger64 CalculateNthPower (UInteger64 n, UInteger64 p);
9/18/2009
1 p 61 (Remember that std::vector<> maintains unordered sequences, so there’s also no ordering comparison, and so no < operator, needed either.) (Remember that std::vector<> maintains unordered sequences, so there’s also no ordering comparison, and so no < operator needed either.) 9/18/2009
1 p 62 Instances can be (in)equality compared with any other instances, and even with themselves. Equality (and inequality) is reflexive, symmetric, and transitive. Instances can be (in)equality compared with any other instances, and even with themselves. Equality is reflexive, symmetric, and transitive. 9/18/2009
1 p 63 6This is another of those “contentious” statements that are going to buy me a lot of flak. Just remember that just because something is widely accepted, and even useful, does not necessarily make it right. There’re plenty of examples of that in this book. 6This is another of those “contentious” statements that are going to buy me a lot of flak. Just remember that just because something is widely accepted, and even useful, does not necessarily make it right. 9/18/2009
1 p 64 7Well, not without a lot of packing pragmas, casting, and other inexcusable hacks. Better to just use the C structure. 7Well, not without a lot of packing pragmas, casting, and other unpleasant hacks. Better to just use the C structure. 9/18/2009
1 p 67 Are we confident that we can avoid (link-time and compile-time) coupling? Again, this is a much underdiscussed aspect of class design. If the entire implementation of the type can be efficiently expressed in an inline definition then we don’t have to worry at all about link-time coupling—the linking to function implementations, either compiled in a separate compilation in the current link-unit, or in external static/dynamic libraries. Either way, we still have to worry about compile-time coupling—the number of other files that must be included in order to compile our class. It’s a cruel irony that often the reduction of link-time coupling can increase compile-time coupling. Are we confident that we can avoid (link-time and compile-time) coupling? Again, this is a much underdiscussed aspect of class design. If the entire implementation of the type can be efficiently expressed in an inline definition then we don’t have to worry at all about link-time coupling—the linking to function implementations, either compiled in a separate compilation unit in the current link-unit, or in external static/dynamic libraries. Either way, we still have to worry about compile-time coupling—the number of other files that must be included in order to compile our class. It’s a cruel irony that often the reduction of link-time coupling can increase compile-time coupling. 9/21/2009
1 p 68 This is the simplest and most efficient manner in which one object can allow another access to the objects that it contains. Put simply, it is part of the documented semantics of the type that it guarantees that the objects that it contains will not live outside its lifetime. This is obvious in the case of composition, as shown in Listing 5.1. This is the simplest and most efficient manner in which one object can allow another access to the objects that it contains. Put simply, it is part of the documented semantics of the container that it guarantees that the objects that it contains will not live outside its lifetime. This is obvious in the case of composition, as shown in Listing 5.1. 9/21/2009
1 p 69 However, this model is vulnerable to undefined behavior in situations where the client code maintains and attempts to use a pointer (or reference) that it acquired after the container has ceased to exist. But as long as the client code is written correctly in respect of this constraint, it is a perfectly respectable access model and is one of the most commonly used. This model is vulnerable to undefined behavior in situations where the client code maintains and attempts to use a pointer (or reference) that it acquired after the container has ceased to exist. But as long as the client code is written correctly in respect of this constraint, it is a perfectly respectable access model and is one of the most commonly used. 9/21/2009
1 p 70 In this case, the buffer pointer values are void*, and also serve as the tokens to return to the store for release, from which they may be subsequently acquired by other client code. Whether you have to check out instances or not, use of the vouched lifetime access model requires the usual mature approach: read the documentation, code in accordance to the documentation, test, level a custard pie at any developer who changes the semantics and not the documentation. In this case, the buffer pointer values are void*, and also serve as the tokens to return to the store for release, from which they may be subsequently acquired by other client code. Whether you have to check out instances or not, use of the vouched lifetime access model requires the usual responsible approach: read the documentation, code in accordance to the documentation, test, level a custard pie at any developer who changes the semantics and not the documentation. 9/21/2009
1 p 70 A simple model, where applicable, is to give the instances to the caller. It isn’t widely useful, but it does find appeal in some circumstances. An example would be when implementing a container that is seldom traversed more than once, and whose elements occupy a large amount of memory and/or other resources. Maintaining copies in these circumstances is likely to be a wasted effort, so they can just be given to the caller. This model is therefore more efficient than the vouched-lifetimes model (see Listing 5.4). A simple model, where applicable, is to give the instances to the caller. It isn’t widely useful, but it does find appeal in some circumstances. An example would be when implementing a container that is seldom traversed more than once, and whose elements occupy a large amount of memory and/or other resources. Maintaining copies in these circumstances is likely to be a wasted effort, so they can just be given to the caller. This model can be more efficient than the vouched-lifetimes model (see Listing 5.4). 9/21/2009
1 p 72 This solution can even work for types that have not been built for reference counting by using a smart pointer type that brings reference-counting logic with it, such as Boost’s shared_ptr. This solution can even work for types that have not been built for reference counting by using a smart pointer type that brings reference-counting logic with it, such as shared_ptr. 9/21/2009
1 p 77 private:
V &m_var;
V m_revert;
. . .
private:
T &m_var;
T m_revert;
. . .
9/21/2009
1 p 79 Rather than specializing, with all the potential junk and effort that goes along with that, you can just define lock_
instance() and unlock_instance() along with your class and Koenig lookup3 (see sections 20.7 and 25.3.3) which will handle the resolution, as can be seen with the thread_mutex class implemented for the Win32 platform (see Listing 6.8).
Rather than specializing, with all the potential junk and effort that goes along with that, you can just define lock_
instance() and unlock_instance() along with your class and rely on Argument-
dependent Lookup3 (see sections 20.7 and 25.3.3) to handle the resolution, as can be seen with the thread_mutex class implemented for the Win32 platform (see Listing 6.8).
9/21/2009
1 p 82 In this class, the locking is inverted by simply swapping the semantics of lock() and unlock(). Now we can change the critical region code back into one, and insert noncritical regions within it (see Listing 6.11). In this class, the locking is inverted by simply swapping the semantics of lock() and unlock(). Now we can change the critical region code back into one, and insert scoped noncritical regions within it (see Listing 6.11). 9/21/2009
1 p 88 Of course, all this would be of minor significance if people would avoid globals as we all know that they should, but I leave it to others to write code for an ideal world; we’ve got to deal with this one. Of course, all this would be of minor significance if people would avoid globals as we all know that they should, but I leave it to other authors to discuss how to write code for an ideal world; we’ve got to deal with this one. 9/21/2009
1 p 90 After Part One, I expect you came out with a warm, happy sensation, seeing C++ in an almost entirely positive light. You’ll need to hold on to those feelings as you read this part (and the next), as we’ll be giving the old girl a bit of a savaging. Several of the issues highlighted are those where C++ does not generally distinguish itself well with respect to other languages. The upside is that there are practical answers to most of the problems highlighted. After Part One, I expect you came out with a warm, happy sensation, seeing C++ in an almost entirely positive light. You’ll need to hold on to those feelings as you read this part (and the next), as we’ll be giving the old girl a bit of a savaging. Several of the issues highlighted are those where C++ does not generally distinguish itself well with respect to other languages. The upside is that there are practical answers to most of the problems raised. 9/21/2009
1 p 93 C is a much simpler language than C++, in line with the spirit of C (see Prologue). It does not have classes, objects, virtual functions, exception-handling, run time type information, and templates. C is a much simpler language than C++, in line with the spirit of C (see Prologue). It does not have classes, objects, virtual functions, exception-handling, run time type information, or templates. 9/21/2009
1 p 93 struct S
{
long l;
char char;
short s;
int i;
};
struct S
{
short s;
long l;
char ch;
int i;
};
9/21/2009
1 p 95 Clearly, the lack of an ABI prevents the provision of a single statically linked version of your library, on the Win32 platform; similar incompatibilities exist on other platforms, although the Itanium standardization means that GCC and Intel can cooperate on Linux. There is a prosaic solution to the problem. For each compiler your clients may wish to use, you need to have access to that compiler, or a compatible one, and produce target libraries aimed at it. Thus, you might create mystuff_gcc.a (mystuff_a.lib for Win32), mystuff_cw.a (mystuff_cw.lib for Win32), and so on. The practical impediment is that you are unlikely to have access to all the compilers you may need, especially if you want to support several operating systems. Even if you do, it is an odious task to maintain all the makefiles/project files—more like hard labor than software engineering. It may be considered reasonable for small projects, but it’s unacceptable for any large project: you can’t imagine writing operating systems in this way! Clearly, the lack of an ABI prevents the provision of a single statically linked version of your library on the Win32 platform; similar incompatibilities exist on other platforms, although the Itanium standardization means that GCC and Intel can cooperate on Linux. There is a prosaic solution to the problem. For each compiler your clients may wish to use, you need to have access to that compiler, or a compatible one, and produce target libraries aimed at it. Thus, you might create mystuff_gcc.a (mystuff_gcc.lib for Windows), mystuff_cw.a (mystuff_cw.lib for Windows), and so on. The practical impediment is that you are unlikely to have access to all the compilers you may need, especially if you want to support several operating systems. Even if you do, it is an odious task to maintain all the makefiles/project files—more like hard labor than software engineering. It may be considered reasonable for small projects, but it’s unacceptable for any large project: you can’t imagine writing operating systems in this way! 9/21/2009
1 p 95 Modern operating systems, and many modern applications, make use of a technique known as dynamic linking, which we look at in detail in Chapter 9. In this case, you link against the library in a similar way, but the code is not copied into the finished executable. Rather, entry points are recorded, and when an executable is loaded by the operating system, the dynamic libraries on which it depends are also loaded and the entry points altered to point into the actual code within the library as it now resides within the new process’s address space. On Win32 systems, the creation of a dynamic library is usually accompanied by the generation of a small static library, known as an import library, which contains the code that the application will use to fix up addresses when the dynamic library is loaded. Executables are linked against such import libraries in the same way as they are with normal (static) libraries.
From the library side, the functions that are made available for dynamic linking are known as export functions. Depending on the compilers and/or operating system, all the functions in the library may be exported, or only those that you explicitly mark in some way, for example, selection via mapfile on Solaris or using _ _declspec(dllexport) modifiers for Win32 compilers.
Modern operating systems, and many modern applications, make use of a technique known as dynamic linking, which we look at in detail in Chapter 9. In this case, you link against the library in a similar way, but the code is not copied into the finished executable. Rather, entry points are recorded, and when an executable is loaded by the operating system, the dynamic libraries on which it depends are also loaded and the entry points altered to point into the actual code within the library as it now resides within the new process’s address space. On Windows systems, the creation of a dynamic library is usually accompanied by the generation of a small static library, known as an import library, which contains the code that the application will use to fix up addresses when the dynamic library is loaded. Executables are linked against such import libraries in the same way as they are with normal (static) libraries.
From the library side, the functions that are made available for dynamic linking are known as export functions. Depending on the compilers and/or operating system, all the functions in the library may be exported, or only those that you explicitly mark in some way, for example, selection via mapfile on Solaris or using _ _declspec(dllexport) modifiers for Windows compilers.
9/21/2009
1 p 97 C++ provides runtime polymorphism via the virtual function mechanism. Although compilers use a common mechanism—virtual function tables—many of them have mutually incompatible schemes. C++ compilers employ much more complex, and proprietary, symbol-naming schemes, almost rendering it impossible to call a C++ binary component built with one compiler from C++ client code built with another. C++ provides runtime polymorphism via the virtual function mechanism. Although compilers use a common mechanism—virtual function tables—many of them have mutually incompatible schemes. C++ compilers employ much more complex, and proprietary, symbol-naming schemes, almost making it impossible to call a C++ binary component built with one compiler from C++ client code built with another. 9/21/2009
1 p 105 There are occasions when you want to declare, and even define, C++ functions in contexts that are going to be surrounded with extern "C" automatically. A good example of this is when using COM IDL. It is not uncommon to define some C++ helper functions, or simple classes, inside the IDL, since in this way the separation between your code and the types and interfaces it uses is minimized. In this case, wrap the code inside a conditionally defined extern "C++" in an order that they will survive the MIDL compiler, which surrounds the entire translations of the interface definitions in extern "C". There are occasions when you want to declare, and even define, C++ functions in contexts that are going to be surrounded with extern "C" automatically. A good example of this is when using COM IDL. It is not uncommon to define some C++ helper functions, or simple classes, inside the IDL, since in this way the separation between your code and the types and interfaces it uses is minimized. In this case, wrap the code inside a conditionally defined extern "C++" in order that they will survive the MIDL compiler, which surrounds the entire translations of the interface definitions in extern "C". 9/21/2009
1 p 105 SomeObject *makeSomeObject()
{
return new Concrete<SomeObject>();
}
extern "C" void CreateSomeObject(SomeObject *pp)
{
*pp = makeSomeObject();
}
SomeObject *MakeSomeObject()
{
return new Concrete<SomeObject>();
}
extern "C" void CreateSomeObject(SomeObject *pp)
{
*pp = MakeSomeObject();
}
9/21/2009
1 p 106 // MLBfrStr.h
_ _SYNSOFT_GEN_OPAQUE(HBuffStr); // Generates a unique handle
HBuffStr BufferStore_Create(Size siBuffer, UInt32 cBuffers);
void BufferStore_Destroy(HBuffStr hbs);
// MLBfrStr.h
SYNSOFT_GEN_OPAQUE(HBuffStr); // Generates a unique handle
HBuffStr BufferStore_Create(Size siBuffer, UInt32 cBuffers);
void BufferStore_Destroy(HBuffStr hbs);
9/21/2009
1 p 110 #define OBJ_CALLCONV . . . // consistent within each OS

struct IObject
{
virtual void OBJ_CALLCONV SetName(char const *s) = 0;
virtual char const *OBJ_CALLCONV GetName() const = 0;
};

extern "C" int make_Object(IObject **pp);
#define OBJ_CALLCONV . . . // consistent within each OS

struct IPerson
{
virtual void OBJ_CALLCONV SetName(char const *s) = 0;
virtual char const *OBJ_CALLCONV GetName() const = 0;
};

extern "C" int make_Person(IPerson **pp);
9/21/2009
1 p 111 IObject *pObject;

if(make_Object(&pObject))
{
pObject->SetName("Reginald Perrin");
std::cout << pObject->GetName() << std::endl;
}
return 0;
}
Dynamic libraries containing an implementation of the factory [Gamm1995, Sutt2000] function make_Object() and executables containing main() were created for each of our six compilers. In this case there’s no need of a compiler comparison table, because all 36 permutations ran perfectly. How’s that for some C++ ABI?
It would appear that we are able to support runtime polymorphism by using an object via a fully abstract class: an interface. The extension of this is to select different make_Object() functions, or to have it return instances of different types dependent on arguments, or other criteria.
IPerson *person;

if(make_Person(&person))
{
person->SetName("Reginald Perrin");
std::cout << person->GetName() << std::endl;
}
return 0;
}
Dynamic libraries containing an implementation of the factory [Gamm1995, Sutt2000] function make_Person() and executables containing main() were created for each of our six compilers. In this case there’s no need of a compiler comparison table, because all 36 permutations ran perfectly. How’s that for some C++ ABI?
It would appear that we are able to support runtime polymorphism by using an object via a fully abstract class: an interface. The extension of this is to select different make_Person() functions, or to have it return instances of different types dependent on arguments, or other criteria.
9/21/2009
1 p 111 Listing 8.2
struct IObject;
struct IObjectVTable
{
void (*SetName)(struct IObject const *obj, char const *s);
char const *(*GetName)(struct IObject const *obj);
};

struct IObject
{
struct IObjectVTable *const vtable;
};
Listing 8.2
struct IPerson;
struct IPersonVTable
{
void (*SetName)(struct IPerson const *person, char const *name);
char const *(*GetName)(struct IPerson const *person);
};

struct IPerson
{
struct IPersonVTable *const vptr;
};
9/21/2009
1 p 112 If you’re not experienced with COM, this may look pretty foreign to you, but it’s actually quite straightforward. It works because fundamentally a class is just a structure, and if it defines virtual functions (or inherits from one that does), then it contains a hidden member called a vptr. The vptr is a pointer to a table (usually shared between all instances of the class) that contains pointers to all the class (virtual) member functions, called a vtable. In this case, the vtable is of type struct IObjectVTable, which contains pointers to the SetName() and GetName() methods. It’s like any function pointer table except that the first parameter to all functions is a pointer to the interface structure—the this pointer in C++. The interface structure struct IObject has a single member vtable which points to its vtable—an instance of struct IObjectVTable. If you’re not experienced with COM, this may look pretty foreign to you, but it’s actually quite straightforward. It works because fundamentally a class is just a structure, and if it defines virtual functions (or inherits from one that does), then it contains a hidden member called a vptr. The vptr is a pointer to a table (usually shared between all instances of the class) that contains pointers to all the class (virtual) member functions, called a vtable. In this case, the vtable is of type struct IPersonVTable, which contains pointers to the SetName() and GetName() methods. It’s like any function pointer table except that the first parameter to all functions is a pointer to the interface structure—the this pointer in C++. The interface structure struct IPerson has a single member vptr which points to its vtable—an instance of struct IPersonVTable. 9/21/2009
1 p 112 struct IObjectVTable
{
uint32_t v1; /* Always 0 */
void *v2; /* Some unknown function */
uint32_t v3; /* Always 0 */
void (*SetName)(struct IObject *, char const *s);
uint32_t v4; /* Always 0 */
char const *(*GetName)(struct IObject const *);
};
struct IPersonVTable
{
uint32_t v1; /* Always 0 */
void *v2; /* Some unknown function */
uint32_t v3; /* Always 0 */
void (*SetName)(struct IPerson *, char const *s);
uint32_t v4; /* Always 0 */
char const *(*GetName)(struct IPerson const *);
};
9/21/2009
1 p 112 struct IObjectVTable
{
void *v1; /* Some unknown function */
void *v2; /* Some unknown function */
void (*SetName)(struct IObject *, char const *s);
char const *(*GetName)(struct IObject const *);
};
struct IPersonVTable
{
void *v1; /* Some unknown function */
void *v2; /* Some unknown function */
void (*SetName)(struct IPerson *, char const *s);
char const *(*GetName)(struct IPerson const *);
};
9/21/2009
1 p 113 void AlterObject(Thing *thing)
{
typedef struct ThingVTable vtable_t;
vtable_t *vt = (vtable_t*)malloc(sizeof(vtable_t));
*vt = *thing->vtable;
vt->Method = someOtherFunction;
thing->vtable = vt;
};
void AlterPerson(IPerson *person)
{
typedef struct IPersonVTable vtable_t;
vtable_t *v = (vtable_t*)malloc(sizeof(vtable_t));
*v = *person->vptr;
*v->SetName = ValidateAndSetName; /* the new function */
*person->vptr = v;
};
9/21/2009
1 p 114 Fortunately, in one of those Eureka “why-didn’t-I-think-of-that-five-years-ago?” moments, I worked out a simple way around this: instead of deducing the format of most compilers’ vtables and working around that, why not define our own vtable format and make all compilers work with that? The result doesn’t look too much different from what we’ve already seen, but it works for all compilers for a given platform. I should warn you, though: even though it’s conceptually nice, it’s not pretty to look at. Consider the fully portable version of our IObject interface in Listing 8.3. Fortunately, in one of those Eureka “why-didn’t-I-think-of-that-five-years-ago?” moments, I worked out a simple way around this: instead of deducing the format of most compilers’ vtables and working around that, why not define our own vtable format and make all compilers work with that? The result doesn’t look too much different from what we’ve already seen, but it works for all compilers for a given platform. I should warn you, though: even though it’s conceptually nice, it’s not pretty to look at. Consider the fully portable version of our IPerson interface in Listing 8.3. 9/21/2009
1 p 114 Listing 8.3
#include <poab/poab.h>
#include <poab/pack_warning_push.h>
#include <poab/pack_push_ambient.h>
struct IObject;
struct IObjectVTable
{
void (*SetName)(struct IObject *obj, char const *s);
char const *(*GetName)(struct IObject const *obj);
};
struct IObject
{
struct IObjectVTable *const vtable;
#ifdef _ _cplusplus
protected:
IObject(struct IObjectVTable *vt)
: vtable(vt)
{}
~IObject()
{}
public:
inline void SetName(char const *s)
{
assert(NULL != vtable);
vtable->SetName(this, s);
}
inline char const *GetName() const
{
assert(NULL != vtable);
return vtable->GetName(this);
}
private:
IObject(IObject const &rhs);
IObject &operator =(IObject const &rhs);
#endif /* _ _cplusplus */
};
#include <poab/pack_pop_ambient.h>
#include <poab/pack_warning_pop.h>
Listing 8.3
#include <poab/poab.h>
#include <poab/pack_warning_push.h>
#include <poab/pack_push_ambient.h>
struct IPerson;
struct IPersonVTable
{
void (*SetName)(struct IPerson *obj, char const *s);
char const *(*GetName)(struct IPerson const *obj);
};
struct IPerson
{
struct IPersonVTable *const vptr;
#ifdef _ _cplusplus
protected:
IPerson(struct IPersonVTable *vt)
: vptr(v)
{}
~IPerson()
{}
public:
void SetName(char const* name)
{
assert(NULL != vptr);
vptr->SetName(this, name);
}
char const *GetName() const
{
assert(NULL != vptr);
return vptr->GetName(this);
}
private:
IPerson(IPerson const &rhs);
IPerson& operator =(IPerson const &rhs);
#endif /* _ _cplusplus */
};
#include <poab/pack_pop_ambient.h>
#include <poab/pack_warning_pop.h>
9/21/2009
1 p 115 IObject(IObject const &rhs)
: vtable(rhs.vtable)
{}
IPerson(IPerson const &rhs)
: vptr(rhs.vptr)
{}
9/21/2009
1 p 115 Third, as a convenience to C++ client code, there are two inline methods defined on
IObject. This means that C++ client code can use a normal syntax, as in:
IObject *obj = . . .
obj->SetName("Scott Tracy");
Third, as a convenience to C++ client code, there are two inline methods defined on
IPerson. This means that C++ client code can use a normal syntax, as in:
IPerson *tb = . . .
tb->SetName("Scott Tracy");
9/21/2009
1 p 116 cranked out by a code generator, or a wizard plug-in to your favorite IDDE. There are plenty frameworks out there that require more complex and arcane necromancy than this. Furthermore, all the complexity lies on the server side. On the client side the code looks exactly as it would when using a normal C++ class virtually.
Nonetheless, as an aid to acceptance, you can, if you choose, wrap this all up in macros.4 I’ve done an example in the code available on the CD, which looks like:
#include "poab_gen.h"

STRUCT_BEGIN(IObject)
STRUCT_METHOD_1_VOID(IObject, SetName, char const *)
STRUCT_METHOD_0_CONST(IObject, GetName, char const *)
STRUCT_END(IObject)
cranked out by a code generator, or a wizard plug-in to your favorite IDE. There are plenty frameworks out there that require more complex and arcane necromancy than this. Furthermore, all the complexity lies on the server side. On the client side the code looks exactly as it would when using a normal C++ class virtually.
Nonetheless, as an aid to acceptance, you can, if you choose, wrap this all up in macros.4 I’ve done an example in the code available on the CD, which looks like:
#include "poab_gen.h"

STRUCT_BEGIN(IPerson)
STRUCT_METHOD_1_VOID(IPerson, SetName, char const *)
STRUCT_METHOD_0_CONST(IPerson, GetName, char const *)
STRUCT_END(IPerson)
9/21/2009
1 p 116 Listing 8.4
#if defined(_ _cplusplus) && \
defined(POAB_COMPILER_HAS_COMPATIBLE_VTABLES)
struct IObject
{
virtual void SetName(char const *s) = 0;
virtual char const *GetName() const = 0;
};
#else /* ? _ _cplusplus */
. . . // The previous definition (Listing 8.3)
#endif /* C++ && portable vtables */
When using such a compiler, the interface is a C++ virtual class. When using another compiler, it uses the portable definition. This is the case on client and/or server sides. This can also be encapsulated within the macros.
Now we can see why providing the convenient inlines in our interface is more than just a convenience. It enables client code to be written for compatible and incompatible compilers alike.
8.2.3  Portable Server Objects
We’ve seen the portable interface and some simple client code. Obviously the bulk of the complexity of this technique is going to lie on the server side, so let’s look at just how bad it is. Listing 8.5 shows the first half of the implementation of a class Object, which implements the interface IObject.
Listing 8.4
#if defined(_ _cplusplus) && \
defined(POAB_COMPILER_HAS_COMPATIBLE_VTABLES)
struct IPerson
{
virtual void SetName(char const *s) = 0;
virtual char const *GetName() const = 0;
};
#else /* ? _ _cplusplus */
. . . // The previous definition (Listing 8.3)
#endif /* C++ && portable vtables */
When using such a compiler, the interface is a C++ virtual class. When using another compiler, it uses the portable definition. This is the case on client and/or server sides. This can also be encapsulated within the macros.
Now we can see why providing the convenient inlines in our interface is more than just a convenience. It enables client code to be written for compatible and incompatible compilers alike.
8.2.3  Portable Server Objects
We’ve seen the portable interface and some simple client code. Obviously the bulk of the complexity of this technique is going to lie on the server side, so let’s look at just how bad it is. Listing 8.5 shows the first half of the implementation of a class Person, which implements the interface IPerson.
9/21/2009
1 p 117 Listing 8.5
class Object
: public IObject
{
Listing 8.5
class Person
: public IPerson
{
9/21/2009
1 p 117 Listing 8.6
#ifndef POAB_COMPILER_HAS_COMPATIBLE_VTABLES
public:
Object()
: IObject(GetVTable())
{}
Object(Object const &rhs)
: IObject(GetVTable())
, m_name(rhs.m_name)
{}
private:
static void SetName_(IObject *this_, char const *s)
{
static_cast<Object*>(this_)->SetName(s);
}
static char const *GetName_(IObject const *this_)
{
return static_cast<Object const*>(this_)->GetName();
}
static vtable_t *GetVTable()
{
Listing 8.6
#ifndef POAB_COMPILER_HAS_COMPATIBLE_VTABLES
public:
Person()
: IPerson(GetVTable())
{}
Person(Person const &rhs)
: IPerson(GetVTable())
, m_name(rhs.m_name)
{}
private:
static void SetName_(IPerson *this_, char const *s)
{
static_cast<Person*>(this_)->SetName(s);
}
static char const *GetName_(IPerson const *this_)
{
return static_cast<Person const*>(this_)->GetName();
}
static vtable_t *GetVTable()
{
9/21/2009
1 p 118 The first thing to note is that both default constructor and copy constructor initialize the vtable member by calling a static method GetVTable(). This method contains a local static instance of the vtable_t member type, for Object it is IObjectVTable. The first thing to note is that both default constructor and copy constructor initialize the vtable member by calling a static method GetVTable(). This method contains a local static instance of the vtable_t member type, for Person it is IPersonVTable. 9/21/2009
1 p 118 The virtual methods themselves are actually the static methods SetName_() and
GetName_(). They each take the this pointer of the given instance as their first argument, this_.5 It’s important to note that this is then downcast to type Object. If this were not done, we’d find ourselves in an infinite loop, as they would call the inline methods defined in IObject.
Let’s look now at the reason why the SetName() and GetName() accessor methods are defined virtual in the class. With compatible compilers, which basically inherit from the bona fide C++ abstract class, these methods would be virtual, since Object would inherit from an IObject that would define them as virtual. Failure to make them virtual for all compilers could represent a serious inconsistency, if you are deriving subclasses from Object, which are then passed to out to client code via the IObject interface. By having them virtual, we can implement the external virtual behavior—what the client sees—in terms of internal virtual behavior.
The virtual methods themselves are actually the static methods SetName_() and
GetName_(). They each take the this pointer of the given instance as their first argument, this_.5 It’s important to note that this is then downcast to type Person. If this were not done, we’d find ourselves in an infinite loop, as they would call the inline methods defined in IPerson.
Let’s look now at the reason why the SetName() and GetName() accessor methods are defined virtual in the class. With compatible compilers, which basically inherit from the bona fide C++ abstract class, these methods would be virtual, since Person would inherit from an IPerson that would define them as virtual. Failure to make them virtual for all compilers could represent a serious inconsistency, if you are deriving subclasses from Person, which are then passed out to client code via the IPerson interface. By having them virtual, we can implement the external virtual behavior—what the client sees—in terms of internal virtual behavior.
9/21/2009
1 p 119 The main problem with the approach so far outlined, as least in my opinion, is the appearance of the infrastructure gunk in the concrete classes implementing the portable interfaces. Thankfully, this is easily rectified by placing it in a related class, which is then used as the base for any concrete class implementations. Hence, we can define a class IObjectImpl in the same header file as IObject, which contains the functions and vtable code that we have placed in Object. For compatible compilers, IObjectImpl will just be a typedef to IObject. Now the whole picture is far more attractive: The main problem with the approach so far outlined, as least in my opinion, is the appearance of the infrastructure gunk in the concrete classes implementing the portable interfaces. Thankfully, this is easily rectified by placing it in a related class, which is then used as the base for any concrete class implementations. Hence, we can define a class IPersonImpl in the same header file as IPerson, which contains the functions and vtable code that we have placed in Person. For compatible compilers, IPersonImpl will just be a typedef to IPerson. Now the whole picture is far more attractive: 9/21/2009
1 p 119 Listing 8.7
// In IObject.h
struct IObject
{
. . .
};
#if defined(__cplusplus) && \
defined(POAB_COMPILER_HAS_COMPATIBLE_VTABLES)
typedef IObject IObjectImpl;
#else /* ? __cplusplus */
. . . The previous definition (Listing 8.3)
class IObjectImpl
: public IObject
{
. . . // function definitions and vtables
};
#endif /* C++ && portable vtables */
Now the definition of any derived class is simple and uncluttered by infrastructure,
as in:
class Object
: public IObjectImpl
{
public:
virtual void SetName(char const *s);
virtual char const *GetName() const;
private:
std::string m_name;
};
Listing 8.7
// In IPerson.h
struct IPerson
{
. . .
};
#if defined(__cplusplus) && \
defined(POAB_COMPILER_HAS_COMPATIBLE_VTABLES)
typedef IPerson IPersonImpl;
#else /* ? __cplusplus */
. . . The previous definition (Listing 8.3)
class IPersonImpl
: public IPerson
{
. . . // function definitions and vtables
};
#endif /* C++ && portable vtables */
Now the definition of any derived class is simple and uncluttered by infrastructure,
as in:
class Person
: public IPersonImpl
{
public:
virtual void SetName(char const *name);
virtual char const *GetName() const;
private:
std::string m_name;
};
9/21/2009
1 p 120 // C client code
IObject *obj;

obj->vtable->SetName(obj, "Archie Goodwin");
printf("Name: %s\n", obj->vtable->GetName(obj));
/* C client code */
IPerson *person;

person->vptr->SetName(person, "Archie Goodwin");
printf("Name: %s\n", person->vptr->GetName(person));
9/21/2009
1 p 120 struct IObjectVTable
{
void (*Destructor)(struct IObject *obj);
};
struct IPersonVTable
{
void (*Destructor)(struct IPerson *obj);
};
9/24/2009
1 p 121 Regarding ownership, there are two approaches generally used. Both use a factory function, such as make_Object(). In one, a corresponding destroy_Object() function is called to return the instance when it is no longer needed. This is a simple and workable model, but it lends itself more to single-use/large-object scenarios, for example, a compiler plug-in. The other approach is to use reference counting. Generally, this is the best technique to go with the portable vtables. Regarding ownership, there are two approaches generally used. Both use a factory function, such as make_Thing(). In one, a corresponding destroy_Thing() function is called to return the instance when it is no longer needed. This is a simple and workable model, but it lends itself more to single-use/large-object scenarios, for example, a compiler plug-in. The other approach is to use reference counting. Generally, this is the best technique to go with the portable vtables. 9/24/2009
1 p 122 Nor can I claim that they represent a full ABI by any stretch of the imagination. For example, you cannot use multiple or virtual inheritance, cannot throw exceptions through ABI barriers, or directly delete objects acquired through them. If you’re writing code that needs to work only with other code written by the same compiler vendor (and version, as that can often need to remain constant), then you’re best off not buying into these techniques. Nor can I claim that they represent a full ABI by any stretch of the imagination. For example, you cannot use multiple or virtual inheritance, cannot throw exceptions through ABI barriers, or directly delete objects acquired through them. If you’re writing code that needs to work only with other code written by the same compiler vendor (and version, as that can often need to remain constant), then you’re best off not using these techniques. 9/24/2009
1 p 123 There are several areas in the use of dynamic libraries that can catch the unwary and especially affect the use of C++. There are several areas in the use of dynamic libraries that can catch the unwary and which especially affect the use of C++. 10/5/2009
1 p 123 Before we delve into those three important issues, I want to talk a little more about C++ functions and dynamic linking. Before we delve into those important issues, I want to talk a little more about C++ functions and dynamic linking. 10/5/2009
1 p 124 Listing 9.1:
printf("%s => %s", path, find_filename(fileName));
printf("%s => %s", filename, ffn(fileName)); 10/5/2009
1 p 125 However, if the implementation of Thing::PrivateMethod is in a dynamic library, you can call it via explicit loading, as in the following code compiled with Visual C++: However, if the implementation of Thing::PrivateMethod is in a dynamic library, you can call it via explicit loading, as in the following code: 10/5/2009
1 p 131 But the significance is that if we now look back at the C++ ABI issue, we can see that if a compiler had a compatible mangling scheme but had incompatible RTTI, object mode, and so on, it would be straightforward to produce programs whose correctness could never be verifiably asserted. But the significance is that if we now look back at the C++ ABI issue, we can see that if a compiler had a compatible mangling scheme but had incompatible RTTI, object model, and so on, it would be straightforward to produce programs whose correctness could never be verifiably asserted. 10/5/2009
1 p 133 The effect is that one of the increments is lost.
The net effect is that one of the increments is lost.
10/5/2009
1 p 133 mov eax,dword ptr [i]
mov eax, dword ptr [i]
10/5/2009
1 p 135 5The volatile qualifiers facilitate the use of volatile as a qualifier on the declaration of any such variables, which is a reasonably common practice in multithreaded code, since it prevents the compiler from manipulating variables in its internal registers, thus failing to synchronize the value with the actual memory location of the variables. We’ll look at it in more detail in section 18.5. 5The volatile qualifiers in the class definition facilitate the use of volatile as a qualifier on the declaration of any such variables, which is a reasonably common practice in multithreaded code, since it prevents the compiler from manipulating variables in its internal registers, thus failing to synchronize the value with the actual memory location of the variables. We’ll look at it in more detail in section 18.5. 10/7/2009
1 p 136 public:
atomic_integer(int value)
: m_value(value)
{}
// Operations
public:
class_type volatile &operator ++() volatile
{
atomic_increment(&m_value);
return *this;
}
const class_type volatile operator ++(int) volatile
{
return class_type(atomic_postincrement(&m_value));
}
class_type volatile &operator —() volatile;
const class_type volatile operator —(int) volatile;

class_type volatile &operator +=(value_type const &value) volatile
{
atomic_postadd(&m_value, value);
return *this;
}
class_type volatile &operator -=(value_type const &value) volatile;

private:
volatile int m_value;
};
public:
atomic_integer(int value)
: m_value(value)
{}
// Operations
public:
atomic_integer volatile &operator ++() volatile
{
atomic_increment(&m_value);
return *this;
}
atomic_integer const volatile ++(int) const volatile
{
return class_type(atomic_postincrement(&m_value));
}
atomic_integer volatile &operator --() volatile;
atomic_integer const volatile operator --(int) const volatile;

atomic_integer volatile &operator +=(int const &value) volatile
{
atomic_postadd(&m_value, value);
return *this;
}
atomic_integer volatile &operator -=(int const &value) volatile;

private:
volatile int m_value;
};
10/7/2009
1 p 137 However, because the costs of the acquire and/or release calls can be very high, the balance between breaking critical regions and the causing long waits for pending threads is a delicate one. However, because the costs of the acquire and/or release calls can be very high, the balance between breaking critical regions and causing long waits for pending threads is a delicate one. 10/7/2009
1 p 137 Mutexes are the most common form of synchronization object for guarding critical regions and, depending on the operating system, can be two kinds: interprocess and intraprocess. Mutexes are the most common form of synchronization object for guarding critical regions and, depending on the operating system, there can be two kinds: interprocess and intraprocess. 10/7/2009
1 p 139 spin_mutex(class_type const &rhs);
spin_mutex &operator =(class_type const &rhs);
spin_mutex(spin_mutex const &);
spin_mutex &operator =(spin_mutex const &);
10/7/2009
1 p 141 10The full implementations for these functions are included on the CD. 10The full implementations for these functions are to be found in the STLSoft libraries, which are included on the CD. 10/7/2009
1 p 142 The s_uniprocessor is true for uniprocessors, and false for multiprocessor machines. The s_uniprocessor is true for uniprocessor machines, and false for multiprocessor machines. 10/7/2009
1 p 142 The first strategy—Unguarded—does no locking, and simply increments or decrements the variable via the ++ or – operators. The first strategy—Unguarded—does no locking, and simply increments or decrements the variable via the ++ or -- operators. 10/7/2009
1 p 143 Indeed, it appears to be about 10 times as fast on the uniprocessor system. However, it has about the same performance on the multiprocessor machine. Indeed, it appears to be about 10 times as fast as the mutex on the uniprocessor system. However, it has about the same performance on the multiprocessor machine. 10/7/2009
1 p 143 Furthermore, on some flavous of Linux, functions with the correct semantics—increment the value and return the previous value—are not available. Furthermore, on some flavors of Linux, functions with the correct semantics—increment the value and return the previous value—are not available. 10/20/2009
1 p 144 But the main lesson is to profile, and to question your assumptions. The main lesson is to profile, and to question your assumptions. 10/20/2009
1 p 146 As was described in section 17.3, compilers have different reactions to for-loop declarations, and if we were to have two synchronized regions in the same scope, some of the older ones would complain.
As is described in section 17.3, compilers have different reactions to for-loop declarations, and if we were to have two synchronized regions in the same scope, some of the older ones would complain.
10/20/2009
1 p 147 #define concat__(x, y) x ## y
#define concat_(x, y) concat__(x, y)
#define SYNCHRONIZED(T, v) \
for(synchronized_lock<lock_scope<T> > \
concat_(__lock__, __LINE__) (v); \
concat_(__lock__, __LINE__); \
concat_(__lock__, __LINE__) .end_loop())
#define CONCAT_BASE_(x, y) x ## y
#define CONCAT_(x, y) CONCAT_BASE_(x, y)
#define SYNCHRONIZED(T, v) \
for(synchronized_lock<lock_scope<T> > \
CONCAT_(__lock__, __LINE__) (v); \
CONCAT_(__lock__, __LINE__); \
CONCAT_(__lock__, __LINE__) .end_loop())
10/20/2009
1 p 149 In reality, one is very unlikely to need to break this limit, but given the increasingly multicomponent nature of software it is by no means impossible. In reality, one is unlikely to need to break this limit, but given the increasingly multicomponent nature of software it is by no means impossible. 10/20/2009
1 p 151 Listing 10.6:
HTssKey Tss_CreateKey( void (*pfnClose)()
HTssKey Tss_CreateKey( void (*pfnClose)(void*) 10/20/2009
1 p 152 The next class is a fair bit more interesting. The next class is more interesting. 10/20/2009
1 p 152 Tss key_func(. . .); TssKey key_func = . . . 10/20/2009
1 p 152 if(NULL == value) if(NULL == thing) 10/20/2009
1 p 152 TssSlotScope(TssKey<T> key, T &value); TssSlotScope(TssKey<T> &key, T &value); 10/20/2009
1 p 159 Stipulating the object files for object1.cpp, object2.cpp, and object3.cpp in that order to the linker, the ordering for several compilers is shown in Table 11.1:
Stipulating the object files for main.cpp, object2.cpp, and object3.cpp in that order to the linker, the ordering for several compilers is shown in Table 11.1:
10/20/2009
1 p 160 One practical measure of this is to insert debugging code into each compilation unit, in debug builds at least, to trace out the initialization order. One practical measure of this is to insert diagnostic logging code into each compilation unit, in debug builds at least, to trace out the initialization order. 10/21/2009
1 p 161 Without the static, the linker would complain about multiple definitions, whereas using inline would just result in all but one version being elided by the linker.
Without the anonymous namespace, the linker would complain about multiple definitions, whereas using inline would just result in all but one version being elided by the linker.
10/21/2009
1 p 161 static CUTracer s_tracer;
} // namespace
Note that this also works if you declare CUTrace() and s_tracer static (see section 6.4), but the anonymous namespace is the better option as long as you don’t need to support any old compiler relics.
CUTracer s_tracer;
} // namespace
Note that this also works if you declare CUTrace() and s_tracer to be static (see section 6.4), but the anonymous namespace is the better option as long as you don’t need to support any old compiler relics.
10/21/2009
1 p 160 However, this is not a concern, since compilers can easily optimize it down to the... This is not a concern, since compilers can easily optimize it down to the... 10/21/2009
1 p 163 Listing 11.5
class Thing
{
public:
Thing &GetInstance()
Listing 11.5
class Thing
{
public:
static Thing &GetInstance()
10/21/2009
1 p 164 Listing 11.6
class Thing
{
public:
Thing &GetInstance()
Listing 11.6
class Thing
{
public:
static Thing &GetInstance()
10/21/2009
1 p 165 Listing 11.7
class Thing
{
public:
Thing &GetInstance()
Listing 11.7
class Thing
{
public:
static Thing &GetInstance()
10/21/2009
1 p 167 Since it will rely, indirectly, on the state of the underlying libraries, we’ll regard them as stateful for the purposes of this discussion. Since such libraries will rely, indirectly, on the state of the underlying libraries, we’ll regard them as stateful for the purposes of this discussion.
10/21/2009
1 p 169 HTssKey CreateKey( void (*pfnClose)()
HTssKey CreateKey( void (*pfnClose)(void*)
10/21/2009
1 p 173 In calm moments, I wonder whether this is rationale, but I just can’t seem to shake my love of the unfettered “atomic” #include. Whether rationale or not, it serves to provide much material for this chapter that (I hope) is germane to the topic of statics. In calm moments, I wonder whether this is rational, but I just can’t seem to shake my love of the unfettered “atomic” #include. Whether rational or not, it serves to provide much material for this chapter that (I hope) is germane to the topic of statics. 10/21/2009
1 p 174 Listing 11.12
class performance_counter
{
. . .
private:
typedef void (*measure_fn_type)(epoch_type&);
static void performance_counter::qpc(epoch_type &epoch)
{
QueryPerformanceCounter(&epoch);
}
static void performance_counter::gtc(epoch_type &epoch)
Listing 11.12
class performance_counter
{
. . .
private:
typedef void (*measure_fn_type)(epoch_type&);
static void qpc(epoch_type &epoch)
{
QueryPerformanceCounter(&epoch);
}
static void gtc(epoch_type &epoch)
10/21/2009
1 p 174 Listing 11.12
class performance_counter
{
. . .
private:
typedef void (*measure_fn_type)(epoch_type&);
static void performance_counter::qpc(epoch_type &epoch)
{
QueryPerformanceCounter(&epoch);
}
static void performance_counter::gtc(epoch_type &epoch)
Listing 11.12
class performance_counter
{
. . .
private:
typedef void (*measure_fn_type)(epoch_type&);
static void qpc(epoch_type &epoch)
{
QueryPerformanceCounter(&epoch);
}
static void gtc(epoch_type &epoch)
10/21/2009
1 p 175 public:
inline void performance_counter::start()
{
measure(m_start);
}
inline void performance_counter::stop();

public:
void start()
{
measure(m_start);
}
10/21/2009
1 p 175 Most of these techniques are conceptually very simple, even if the implementation of them can be tedious (i.e., API-based singletons).

Most of these techniques are conceptually very simple, even if the implementation of them can be involved (i.e., API-based singletons).
10/21/2009
1 p 187 The way to get around this is to use a local static, as in Listing 18.2, and so the system call is done once, but your compiler still cannot optimize it all away.
The way to get around this is to use a local static, as in Listing 12.2, and so the system call is done once, but your compiler still cannot optimize it all away.
10/21/2009
1 p 193 Using byte_t (const)* s means that we can express pointers to opaque bytes in a simple, readable way, and we don’t have to resort to any casting with pointer arithmetic, so the code is immune to pointer-type mismatch and offset problems.
Using byte_t (const)* means that we can express pointers to opaque bytes in a simple, readable way, and we don’t have to resort to any casting with pointer arithmetic, so the code is immune to pointer-type mismatch and offset problems.
10/21/2009
1 p 195 It also provides minimum-sized integers, e.g., int_least64_t, and fastest integers of a minimum size, e.g., sint_
fastt16_t.)
It also provides minimum-sized integers, e.g., int_least64_t, and fastest integers of a minimum size, e.g., sint_
fast16_t.)
10/21/2009
1 p 196 int8_t i8 = 0; int8_t si8 = 0;
10/21/2009
1 p 206 If we change the dimension of ar without making a corresponding exact change to the third argument in the call to strnset()then, the program would fail to fill the entire array, or possibly worse, overwrite objects other than the array.

then, should not be in mono font.
If we change the dimension of ar without making a corresponding exact change to the third argument in the call to strnset() then, the program would fail to fill the entire array, or possibly worse, overwrite objects other than the array.

10/22/2009
1 p 209 Well, it’s always nice to discover new ways to detect and enforce characteristics (see Chapter 12); generic programming’s here now, and it’s not going away. Well, it’s always nice to discover new ways to detect and enforce characteristics (see Chapter 1); generic programming’s here now, and it’s not going away. 10/22/2009
1 p 210 int ai[NUM_ELEMENTS(vi)]; // Compiler rejects when using new
NUM_ELEMENTS
int ai[NUM_ELEMENTS(vi)]; // Compiler rejects when using new form
10/22/2009
1 p 212 ...but for modern ones use the following combination of a macro, a structure, and a function:
...but for modern ones use the following combination of a macro, a structure template, and a function:
10/22/2009
1 p 213 for(size_t i = 0; i < dimensionof(ar); ++i) for(size_t i = 0; i < NUM_ELEMENTS(ar); ++i) 10/22/2009
1 p 219 process_array(apb, 10); // Ok, but was it worth the effort?
process_array(apb, dimensionof(ad)); // Ok, but was it worth the effort?
10/22/2009
1 p 224 10The C99 standard introduced Variable Length Arrays (VLAs) into the C language, which support multidimensional arrays whose dimension extents can be determination at run time. 10The C99 standard introduced Variable Length Arrays (VLAs) into the C language, which support multidimensional arrays whose dimension extents can be determinated at run time. 10/22/2009
1 p 236 Three solutions are possible. First, the language could dictate, or compiler vendors could opt to assume, that any conditional statement involving the literal true be rewritten in the obverse using the literal false. Three solutions are possible. First, the language could dictate, or compiler vendors could opt to assume, that any conditional statement involving the literal true be translated in the obverse using the literal false. 10/22/2009
1 p 237 Add text before 15.4 Literals Recommendation: Do not use literal true in conditional expressions. Avoid using literal false in conditonal expressions. 10/22/2009
1 p 240 Remove the double undersccore before each SYNSOFT for all code lines on this page.

__SYNSOFT
Should be SYNSOFT 10/22/2009
1 p 240 Add text at end of bottom of page. Recommendation: Use macros to abstract away differences for 64-bit integer literals. 10/22/2009
1 p 241 void interpret(char const *s) Type interpret(char const *s) 10/22/2009
1 p 244 frame_array_d2<int, SOME_CONST, SOME_CONST> ar; // ok
static_array_d2<int, SOME_CONST, SOME_CONST> ar; // ok
10/22/2009
1 p 245 frame_array_d2<int, g_z.z, g_z.z); // error
Furthermore, the constant objects are not created at compile time rather at run time.9 Since they have static scope (i.e., they exist outside the scope of any particular function), they must be constructed (and destroyed) during program execution. You need to be aware that there are three potential drawbacks to their use in this regard:
First, the constructors and destructor for the particular type may have significant run time cost. This is borne at application/module start-up and shutdown, where such costs are usually, though not always, of concern.
static_array_d2<int, g_z.z, g_z.z>; // error
Furthermore, the constant objects are not created at compile time, but at run time.9 Since they have static scope (i.e., they exist outside the scope of any particular function), they must be constructed (and destroyed) during program execution. You need to be aware that there are three potential drawbacks to their use in this regard:
First, the constructors and destructor for the particular type may have significant run time cost. This is borne at application/module start-up and shutdown, where such costs are usually, though not always unimportant.
10/22/2009
1 p 248 Notwithstanding this aspect, using enum for contants this is a very well-used technique, and quite successful where applicable. Notwithstanding this aspect, using enum for constants is a very well-used technique, and quite successful where applicable. 10/22/2009
1 p 251 The problem is that one thread could have just in-place constructed s_category but not yet have set the hidden flag variable. The problem is that one thread could have just in-place constructed s_category but not yet have set the hidden flag variable (see section 11.3). 10/22/2009
1 p 251 That’s because once any thread completes step 2, any subsequent thread entering the function for the entire duration of the process will have well-defined behavior. That’s because once any thread assigns to the hidden flag variable, any subsequent thread entering the function for the entire duration of the process will have well-defined behavior. 10/22/2009
1 p 254 Nonetheless, I think it’s worthwhile to have highlighted the issues, since they are often used but little discussed. In section 8.2.7 we looked at a related keyword that would be much more useful to have, pinterface.
16.1.1  pinterface
Although I may think that there’s probably no good way of defining an interface keyword, I would suggest there’s a pretty strong case for some other keyword, for example, pinterface, by which all the effective but incredibly verbose gunk of the portable vtables technique (see Chapter 8) could be encapsulated.
Nonetheless, I think it’s worthwhile to have highlighted the issues, since they are often used but little discussed.
16.1.1  pinterface
Although I may think that there’s probably no good practical way of defining an interface keyword, I would suggest there’s a pretty strong case for some other keyword, for example, pinterface, by which all the effective but incredibly verbose gunk of the portable vtables technique (see Chapter 8) could be encapsulated.
10/22/2009
1 p 255 p holds a pointer to the internal buffer of s,1 but that pointer is valid only so long as s remains unchanged. p holds a pointer to the contents of s,1 but that pointer is valid only so long as s remains unchanged. 10/22/2009
1 p 264 std::string s(“A string object);
std::string s(“A string object”);
10/22/2009
1 p 265 Understandably, the compiler cannot have this level of intuition, as it would be too easy to tie it up in knots: What if both the intermediate types had the same implicit conversion type? Understandably, the compiler cannot have this level of intuition, as it would be too easy to tie it up in knots: What if two intermediate types had the same implicit conversion type? 10/22/2009
1 p 268 typename /* 2 */ T::size_type get_sizeof(T const &t) typename /* 3 */ T::size_type get_sizeof(T const &t) 10/22/2009
1 p 269 typename /* 3 */ T::size_type si = sizeof(t);
return si;
}
Contexts 2 and 3 are used to inform the compiler that the particular identifier being qualified is a type of the given type, rather than, a member.
typename /* 2 */ T::size_type si = sizeof(t);
return si;
}
Contexts 2 and 3 are used to inform the compiler that the particular identifier being qualified is a type of the given type, rather than a member.
10/22/2009
1 p 269 ...from a correct interpretation of the template—at last not with any that I have ever presented to... ...from a correct interpretation of the template—at least not with any that I have ever presented to... 10/22/2009
1 p 274 (Note that this does not mean they have to be explicitly coerced to Boolean type; that would be inefficient—see section 1.4.2). (Note that this does not mean they have to be explicitly coerced to bool type; that would be inefficient—see section 1.4.2.) 10/23/2009
1 p 275 Couple this with the ability to place assignment expressions in within conditional statements, and we have a recipe for disaster. Couple this with the ability to place assignment expressions within conditional statements, and we have a recipe for disaster. 10/23/2009
1 p 277 The rule change was to place the initialiser statement variables within the scope of the for statement, as in Listing 17.5. The rule change was to place the initializer statement variables within the scope of the for statement, as in Listing 17.5. 10/23/2009
1 p 280 If you think that this rule is contrived, consider the case where you need to iterate through two or more containers, of different types, manually rather than using std::for_each;... If you think that this rule is contrived, consider the case where you need to iterate through two or more containers, of different types, manually rather than using an algorithm;... 10/23/2009
1 p 283 These are the notations you’ll see used in code throughout the book. These are the notations you’ll see used in code throughout the book.

The fourth, which I’ve encountered in one former employer’s code base, was X_m. Since I like to use a trailing _t for types, I don’t favor this scheme.
10/23/2009
1 p 294 Imperfection: C++ provides no type safety for the use of conceptual defined types, except where such types have incompatible underlying types. Imperfection: C++ provides no type safety for the use of conceptually defined types, except where such types have incompatible underlying types. 10/23/2009
1 p 294 When ported to another platform two of more of the underlying types may now be equivalent, and the code will no longer build. When ported to another platform two or more of the underlying types may now be equivalent, and the code will no longer build. 10/23/2009
1 p 297 Now when we attempt to construct a Socket with family and type parameters in the wrong order, the compiler informs us that they parameters are incompatible, and we can fix the bug immediately. Now when we attempt to construct a Socket with family and type parameters in the wrong order, the compiler informs us that the parameters are incompatible, and we can fix the bug immediately. 10/23/2009
1 p 298 true_typedef<int, . . .> i1 = 1000;
int i2 = 1001;
true_typedef<int, . . .> i3 = i2;
true_typedef<int, . . .> i1(1000);
int i2(1001);
true_typedef<int, . . .> i3(i2);
10/23/2009
1 p 300 For example, it is a matter of habit when I define any class that inherits from another that I declare a typedef base_class_type, as in:
For example, it is a matter of habit when I define any class that inherits from another that I declare a typedef parent_class_type, as in:
10/23/2009
1 p 302 Text added. Recommendation: Use parent_class_type for maintenance robustness. 10/26/2009
1 p 309 Bolt-ins “bolt in” enhancing functionality over existing, and often complete, types. Bolt-ins “bolt in” enhancing functionality over existing, though usually incomplete, types. 10/26/2009
1 p 309 1Since this is software engineering, and not cycling, you probably want to steer clear of the carbs, which might make you sleepy, and instead get come coffee down you. You’ll need all your attention in this part. 1Since this is software engineering, and not cycling, you probably want to steer clear of the carbs, which might make you sleepy, and instead get some coffee down you. You’ll need all your attention in this part. 10/26/2009
1 p 311 The problems, from the point of view of accuracy, of conversions were mentioned in Chapter 13, and are described in detail in [Stro1997]. The problems, from the point of view of accuracy, of numeric conversions were mentioned in Chapter 13, and are described in detail in [Stro1997]. 10/26/2009
1 p 314 Outside these circumstances, the use of C-style casts should be, avoided. Outside these circumstances, the use of C-style casts should be avoided. 10/26/2009
1 p 328 Listing 19.14
IImpCpp *ic = . . .;
Listing 19.14
IImpC *ic = . . .;
10/26/2009
1 p 330 In such circumstances, the default exception used by interface_cast_noaddref can be set (via the preprocessor) to one that does not actually throw, allowing the above form to be succinct, safe, and yet lightweight. This may not be “proper” C++ but we’re imperfect practitioners, and it’s a pragmatic increase in code quality within the limitations of the technological environment.
In such circumstances, the default exception policy used by interface_cast_noaddref can be set (via the preprocessor) to one that does not actually throw, allowing the above form to be succinct, safe, and yet lightweight. This may not be “proper” C++ but we’re imperfect practitioners, and it’s a pragmatic increase in code quality within the limitations of the specific technological environment. 10/26/2009
1 p 331 func(interface_cast_noaddref<IX>(p)); // Compile error func(interface_cast_noaddref<IX>(py)); // Compile error 10/26/2009
1 p 337 Derived *d = boost::polymorphic_cast<Derived*>(a);
Derived *d = boost::polymorphic_cast<Derived*>(b);
10/26/2009
1 p 338 ptr_cast(pointer_type pt)
: m_p(t)
{}
ptr_cast(reference_type t)
: m_p(&t)
ptr_cast(pointer pt)
: m_p(pt)
{}
ptr_cast(reference t)
: m_p(&t)
10/26/2009
1 p 340 For run time failure, it can be unclear clear whether returning a null value or throwing an exception is the best choice. For run time failure, it can be unclear whether returning a null value or throwing an exception is the best choice. 10/26/2009
1 p 344 The values returned from attribute shims are always valid outside the instance of the shim, in cases where the shim is implemented by the creation of a temporary object.
The values returned from attribute shims are always valid outside the lifetime of the shim, even in cases where the shim is implemented by the creation of a temporary object. 10/26/2009
1 p 345 4I’m with James L. Brooks [Broo1995] on this one. You will always write two versions, so don’t try to do the ultimate generic version in the first. 4I’m with Frederick P. Brooks [Broo1995] on this one. You will always write two versions, so don’t try to do the ultimate generic version in the first. 10/26/2009
1 p 346 The names of logical shims take the form of an interrogative coupled with the particular attribute or state being queried. The names of logical shims take the form of an interrogative element coupled with the particular attribute or state being queried. 10/26/2009
1 p 346 Note that because of the standardized nature, and the large and increasingly established role of the standard library, the is_empty() shim general case—the second one in the list above—is defined in anticipation of its being applied to the standard library containers. Note that because of the standardized nature, and the large and increasingly established role of the standard library, the is_empty() shim general case—the first one in the list above—is defined in anticipation of its being applied to the standard library containers. 10/26/2009
1 p 348 7Be aware that shims are not the be all and end all. See section 10.11 for a note of realism/caution.
7Be aware that shims are not the be all and end all. See section 20.11 for a note of realism/caution.
10/26/2009
1 p 349 ...these two are the type used to store the copy of the string to be tokenized (S) and the type of the iterator’s value_type (V).
...these two are the type used to store the copy of the string to be tokenized (S) and the type of the iterator’s delimiter (D).
10/26/2009
1 p 352 current_directory_scope<char> scope2(dir3); // ok current_directory_scope<char> scope3(dir3); // ok 10/26/2009
1 p 357 20.7  Namespaces and Koenig Lookup
20.7  Namespaces and Argument-Dependent Lookup
10/26/2009
1 p 357 If all the types for which the shim you’re using is defined are user-defined types, and are defined within the namespaces of their “shimmed” type, and the compiler(s) you are using support(s) Koenig lookup,15 then you do not need to specify any using declarations, and the whole thing works like a treat. Got all that?
Koenig lookup (C++-98: 3.4.2)—also known as Argument-dependent Lookup [Vand2003])
—is the mechanism whereby symbols from other namespaces may be accessed in another namespace, without being introduced via using declarations or directives (or typedef declarations), by virtue of being associated with a symbol that has been introduced.
If all the types for which the shim you’re using is defined are user-defined types, and are defined within the namespaces of their “shimmed” type, and the compiler(s) you are using support(s) Argument-Dependent Lookup (ADL) [Vand2003],15 then you do not need to specify any using declarations, and the whole thing works like a treat. Got all that?
Argument-dependent Lookup (C++=98: 3.4;2)—is the mechanism whereby symbols from other namespaces may be accessed in another namespace, without being introduced via using declarations or directives (or typedef declarations), by virtue of being associated with a symbol that has been introduced.
10/26/2009
1 p 357 Unfortunately, this being the real world, several compilers do not fully implement Koenig lookup. Unfortunately, this being the real world, several compilers do not fully implement ADL. 10/26/2009
1 p 357 15Digital Mars prior to v8.34, Visual C++ prior to v7.1, and Watcom do not support Koenig lookup 15Digital Mars prior to v8.34, Visual C++ prior to v7.1, and Watcom do not fully support ADL. 10/26/2009
1 p 358 With the majority of the time this is simple both to do and to understand, but there are occasional confusions when dealing with heavily derivative code.
For the majority of cases, this is simple both to do and to understand, but there are occasional confusions when dealing with heavily derivative code. 10/26/2009
1 p 358 For any components using the shims that are defined within the stlsoft namespace, or within any of its sub-namespaces [Wils2003b], they pick up the necessary shim definitions even when Koenig lookup does not apply (whether that is because the type is a fundamental type or whether the compiler does not support it). For any components using the shims that are defined within the stlsoft namespace, or within any of its sub-namespaces [Wils2003b], they pick up the necessary shim definitions even when ADL does not apply (whether that is because the type is a fundamental type or whether the compiler does not support it). 10/26/2009
1 p 359 #if !defined(ACMELIB_COMPILER_SUPPORTS_KOENIG_LOOKUP)
using third_party::c_str_ptr; // for tp_string
#endif /* ! ACMELIB_COMPILER_SUPPORTS_KOENIG_LOOKUP */
#if !defined(ACMELIB_COMPILER_SUPPORTS_ADL)
using third_party::c_str_ptr; // for tp_string
#endif /* ! ACMELIB_COMPILER_SUPPORTS_ADL */
10/26/2009
1 p 359 The first using declaration is necessary because LSA_UNICODE_STRING type is defined in the global namespace, so Koenig lookup does not apply. The second using declaration is only needed when the compiler does not support Koenig lookup.
The first using declaration is necessary because LSA_UNICODE_STRING type is defined in the global namespace, so ADL does not apply. The second using declaration is only needed when the compiler does not support ADL. 10/26/2009
1 p 360 In this way, the classes and/or functions that constitute a particular shim do not become bloated with accreted unnecessary or unspecific baggage, which is something that (regrettably) happens to traits.
Hence, the classes and/or functions that constitute a particular shim do not become bloated with accreted unnecessary or unspecific baggage, which is something that (regrettably) happens to traits.
10/26/2009
1 p 361 The second set is smaller, and more subject to the keen-eyed instincts of experienced C++ programmers,17 and non-conformance is much more rare but it is still possible.
The second set is smaller, and more subject to the keen-eyed instincts of experienced C++ programmers,17 so non-conformance, though possible, is much more rare.
10/26/2009
1 p 369 template <typename T1, typename T2, typename T3>
MSG_BASE(AIID_t const &aiid . . . T1 const &a2, T1 const &a3)
template <typename T1, typename T2, typename T3>
MSG_BASE(AIID_t const &aiid . . . T2 const &a2, T3 const &a3)
10/26/2009
1 p 376 Second, it may be the replacement of existing functionality. This can be via the well-known C+ mechanism of run time polymorphism [Stro1997], whereby derived class may override the virtual methods of their parent class(es). Second, it may be the replacement of existing functionality. This can be via the well-known C++ mechanism of run time polymorphism [Stro1997], whereby derived class may override the virtual methods of their parent class(es). 10/26/2009
1 p 380 class RefCounter
: public T
{
RefCounter::RefCounter()
: m_cRefs(C::Count)
{
InitSync(this);
}
RefCounter(RefCounter const &rhs)
: ParentClass(rhs)
, m_cRefs(C::Count)
{
class RefCounter
: public T, public S
{
RefCounter::RefCounter()
: m_cRefs(C::Count)
{
InitSync(this);
}
RefCounter(RefCounter const &rhs)
: T(rhs)
, m_cRefs(C::Count)
{
10/26/2009
1 p 381 template <typename T>
struct RecursiveBoltIn
{
void DoIt()
{
static_cast<T*>(this)->Do();
}
};
RecursiveBoltIn is a not a bolt-in, because it doesn’t derive from its parameterizing type.
template <typename T>
struct ReverseBoltIn
{
void DoIt()
{
static_cast<T*>(this)->Do();
}
};
ReverseBoltIn is a not a bolt-in, because it doesn’t derive from its parameterizing type.
10/26/2009
1 p 385 Chapter 23
Template Constructors

Change running heads too.
Chapter 23
Constructor Templates
10/26/2009
1 p 388 What’s worse is that many compilers will actually compile this,2 without warning, with the first version of the Wrapper, This leaves s1 holding onto a non-const reference to a temporary that, once the constructor returns, no longer exists! Buenas noches, Señor Core-Dump!
23.3  Template Constructor Specialization
What’s worse is that many compilers will actually compile this,2 without warning, with the first version of the Wrapper. This leaves s1 holding onto a non-const reference to a temporary that, once the constructor returns, no longer exists! Buenas noches, Señor Core-Dump!
23.3  Constructor Template Specialization
10/26/2009
1 p 392 23.6  Template Constructors: Coda
23.6  Constructor Templates: Coda
10/26/2009
1 p 393 These things are easily done with scripting langauges. These things are easily done with scripting languages. 10/26/2009
1 p 402 operator Boolean const*() const operator boolean const*() const 10/26/2009
1 p 403 Now compilers that support operator int boolean::*() const and those that merely support operator Boolean const*() const are catered for equally well by the operator_bool_generator template. Now compilers that support operator int boolean::*() const and those that merely support operator boolean const*() const are catered for equally well by the operator_bool_generator template. 10/26/2009
1 p 409 Running head: 25.1  fast_strong_concatenator<>

global change
25.1  fast_string_concatenator<> 10/26/2009
1 p 410 There’s more to it, such as concatenation seeding (see section 24.4) and pathological bracing (see section 25.5), but that’s essentially it for the operators in normal circumstances.
There’s more to it, such as concatenation seeding (see section 25.4) and pathological bracing (see section 25.5), but that’s essentially it for the operators in normal circumstances.
10/26/2009
1 p 419 Even if you write your own concatenator-returning overloads in a “nearer” namespace, Koenig lookup (see sections 6.2 and 20.7) will ensure that the compiler can see multiple equivalent operators, and it will rightly fail to compile. Even if you write your own concatenator-returning overloads in a “nearer” namespace, ADL (see sections 6.2 and 20.7) will ensure that the compiler can see multiple equivalent operators, and it will rightly fail to compile. 10/26/2009
1 p 420 The result of the first concatenation is fast_string_
concatenator<String>, so the next operator is deduced (via Koenig lookup) to be one of the standard ones in the concatenator’s namespace.
The result of the first concatenation is fast_string_
concatenator<String>, so the next operator is deduced (via ADL) to be one of the standard ones in the concatenator’s namespace.
10/26/2009
1 p 422 The binary form, which is the bitwise OR operator, is an entirely different beast.
The binary form, which is the bitwise AND operator, is an entirely different beast.
10/26/2009
1 p 429 Consider the amount of coding time, thinking time, and debugging time that is expended trying to understand and work with libraries that use it, I struggle to imagine how using it helps the software engineering community.3
In short, don’t do it. In grepping through all my source databases at the time of writing, I found eleven uses of it. Of the three that were used in “proper” classes—that is, those that are not in utility or meta-programming classes—I can probably truly justify only one of them. I removed two immediately.4 The third I cannot justify, but I’m keeping it for reasons of expediency. For grins, I’ll describe this in the following subsection.
Considering the amount of coding time, thinking time, and debugging time that is expended trying to understand and work with libraries that use it, I struggle to imagine how using it helps the software engineering community.3
In short, don’t do it. In grepping through all my source databases at the time of writing, I found eleven uses of it. Of the four that were used in “proper” classes—that is, those that are not in utility or meta-programming classes—I can probably truly justify only one of them. I removed two immediately.4 The fourth I cannot justify, but I’m keeping it for reasons of expediency. For grins, I’ll describe this in the following subsection.
10/26/2009
1 p 429 Performing arithmetic using the FILETIME structure is tiresome, to say the least. On little-endian systems, the layout is identical to that of ULARGE_INTEGER, so that one can cast instances of one type to the other; hence one can manipulate two subtract FILETIME structures by casting them to ULARGE_INTEGER and subtracting the QuadPart members.
Performing arithmetic using the FILETIME structure is tiresome, to say the least. On little-endian systems, the layout is identical to that of ULARGE_INTEGER, so that one can cast instances of one type to the other; hence one can subtract two FILETIME structures by casting them to ULARGE_INTEGER and subtracting the QuadPart members. 10/26/2009
1 p 430 reinterpret_cast<ULARGE_INTEGER&)(ft3).QuadPart =
reinterpret_cast<ULARGE_INTEGER&)(ft1).QuadPart -
reinterpret_cast<ULARGE_INTEGER&)(ft2).QuadPart;
reinterpret_cast<ULARGE_INTEGER&>(ft3).QuadPart =
reinterpret_cast<ULARGE_INTEGER&>(ft1).QuadPart -
reinterpret_cast<ULARGE_INTEGER&>(ft2).QuadPart;
10/26/2009
1 p 430 return static_cast<FILETIME*>(p);
}
operator FILETIME const *() const;
operator ULARGE_INTEGER *()
{
return static_cast<ULARGE_INTEGER*>(p);

return static_cast<FILETIME*>(m_p);
}
operator FILETIME const *() const;
operator ULARGE_INTEGER *()
{
return static_cast<ULARGE_INTEGER*>(m_p);
10/26/2009
1 p 438 X &operator ++(); // pre-increment
X &operator —(); // pre-decrement
X operator ++(int); // post-increment
X operator —(int); // post-decrement

X &operator ++(); // pre-increment
X &operator --(); // pre-decrement
X operator ++(int); // post-increment
X operator --(int); // post-decrement
10/26/2009
1 p 441 public:
explicit unused_return_value_monitor(R value, M monitor = M())
: m_value(value)
, m_monitor(monitor)
, m_bUsed(false)
{}
unused_return_value_monitor(class_type const &rhs)
: m_value(rhs.m_value)
, m_monitorFn(rhs.m_monitorFn)
, m_bUsed(rhs.m_bUsed)
public:
unused_return_value_monitor(R value, M monitor = M())
: m_value(value)
, m_monitor(monitor)
, m_bUsed(false)
{}
unused_return_value_monitor(class_type const &rhs)
: m_value(rhs.m_value)
, m_monitor(rhs.m_monitor)
, m_bUsed(rhs.m_bUsed)
10/26/2009
1 p 445 Specifying an argument of any unsigned type smaller than 64 bits—again, we’re assuming that int is 32 bits here—will resolve to the uint32_t overload. Specifying an argument of any unsigned type smaller than 64 bits—again, we’re assuming that int is 32 bits here—will resolve to the uint32_t overload.

int should be in mono font.
10/26/2009
1 p 449 The issues we’ve examine for uinteger64 / UInteger64 largely apply to the signed version, except that we no longer need to provide a dubious int overload in order to cater for literals. The issues we’ve examined for uinteger64 / UInteger64 largely apply to the signed version, except that we no longer need to provide a dubious int overload in order to cater for literals. 10/26/2009
1 p 451 Note that there’s still no truncation warning by the compiler, but that’s not needed because we won’t be using the large integer types in the same way as built-in types syntactically.
Note that there’s still no truncation warning by the compiler, but that’s not needed because we won’t be using the large integer types in the same syntactic form as built-in types. 10/26/2009
1 p 453 The && and || operators provide a Boolean evaluation of the two expressions, as in:
In C and C++, the && and || operators provide a Boolean evaluation of two expressions, as in: 10/26/2009
1 p 455 We end up finding a real value in garbage-collected systems that even the grand skeptics—such as me—cannot refute.
We find a real value in garbage-collected systems that even the grand skeptics—such as me—cannot refute.
10/26/2009
1 p 455 This is one of my favorite chapters in the book,2 and it really does speak strongly to how powerful and adaptable this language really is.
This is one of my favorite chapters in the book,2 and it speaks strongly to how powerful and adaptable this language really is. 10/26/2009
1 p 455 2If you think that’s because it’s the biggest, and I’m just an old windbag, well . . . there may some merit in that. 2If you think that’s because it’s the biggest, and I’m just an old windbag, well . . . there may be some merit in that. 10/26/2009
1 p 465 Listing 31.6
PCAChar LongToStringA(Long value)
{
const size_t I2S_LIMIT = 0x7f;
TssValue value = Tss_GetSlotValue(sg_hkeyA);
PAChar buffer;
if(value == 0)
{
value = (TssValue)Mem_Alloc_NoTrack(
sizeof(AChar) * (1 + I2S_LIMIT)));
. . .
Tss_SetSlotValue(sg_hkeyA, value, NULL);
}
buffer = SyCastRaw(PAChar, value);
return integer_to_string(buffer, 1 + I2S_LIMIT, value);
}
2If you think that’s because it’s the biggest, and I’m just an old windbag, well . . . there may be some merit in that. 10/26/2009
1 p 475 buffer[index] = '\0'; stack_buffer[index] = '\0'; 10/26/2009
1 p 479 value_type m_internal[space]; // Internal storage value_type m_internal[SPACE]; // Internal storage 10/26/2009
1 p 479 : m_buffer((space < cItems) ? alloc_(cItems) : &m_internal[0]) : m_buffer((SPACE < cItems) ? alloc_(cItems) : &m_internal[0]) 10/26/2009
1 p 480 STATIC_ASSERT(space != 0);
STATIC_ASSERT( offsetof(class_type, m_buffer)
< offsetof(class_type, m_cItems));
constraint_must_be_pod(value_type);
}
~auto_buffer()
{
if(space < m_cItems)
STATIC_ASSERT(SPACE != 0);
STATIC_ASSERT( offsetof(class_type, m_buffer)
< offsetof(class_type, m_cItems));
constraint_must_be_pod(value_type);
}
~auto_buffer()
{
if(SPACE < m_cItems)
10/26/2009
1 p 484 But remember that auto_buffer does support non-POD types, and it does not initialize the memory it acquires. But remember that auto_buffer does not support non-POD types, and it does not initialize the memory it acquires. 10/26/2009
1 p 484 10As auto_buffer has become more established and widely used, it’s been upgraded with resize() and swap() methods (see Listng 32.1), which makes it much more useful when writing exception safe classes (that use construct-and-swap [Sutt2000]). These enhancements don’t affect its performance and fundamental simplicity. 10As auto_buffer has become more established and widely used, it’s been upgraded with resize() and swap() methods (see Listing 32.1), which makes it much more useful when writing exception safe classes (that use construct-and-swap [Sutt2000]). These enhancements don’t affect its performance and fundamental simplicity. 10/26/2009
1 p 487 We saw in Chapter 9 that passing memory between link units is an unappealing prospect whether we require all link units to share the same underlying allocator, or we require the client code of each link unit to return every allocated piece of memory from whence it came. We saw in Chapter 9 that passing memory between link units is an unappealing prospect whether we require all link units to share the same underlying allocator, or we require the client code of each link unit to return every allocated piece of memory whence it came. 10/26/2009
1 p 487 Finally, you can de-/re-allocate in client code something that was allocated within the library, which can be a useful simplification in many circumstances. Finally, you can de-/re-allocate in client code something that was allocated within the library, which can be a useful simplification in rare circumstances. 10/26/2009
1 p 488 If you’re really unlucky, such a combination might actually work with some compilers and/or operating environments, postponing maintenance conundrums for when your memory has faded and your timescales have shortened. If you’re really unlucky, such a combination might actually work with some compilers and/or operating environments, postponing maintenance conundrums for when your memory has faded and your timescales have shortened. This helps you avoid that trap. 10/26/2009
1 p 495 Not that I’m not saying that the approach of multi_array is wrong, merely that for my multidimensional requirements, and for my way of thinking, it is suboptimal. I’m not saying that the approach of multi_array is wrong, merely that for my multidimensional requirements, and for my way of thinking, it is suboptimal. 10/26/2009
1 p 498 5Like operator [], it performs DbC (see section 12.3) precondition testing via assertions in debug builds. 5Like operator [], it performs DbC (see section 11.3) precondition testing via assertions in debug builds. 10/26/2009
1 p 507 We’ve seen that there are dangers in treating any arrays types in precisely the same way as we often (mis)use built-in arrays, but that by using std::fill_n() and the array_size() shim, we can safely write generic code that will work with all array types.
We’ve seen that there are dangers in treating any array’s types in precisely the same way as we often (mis)use built-in arrays, but that by using std::fill_n() and the array_size() shim, we can safely write generic code that will work with all array types.
10/26/2009
1 p 508 It is possible in many cases to follow the example of istream_iterator and encapsulate the start of the enumeration in the constructor of the nondefault iterator; Boost’s file-system enumeration component works in this way. It is possible in some cases to follow the example of istream_iterator and encapsulate the start of the enumeration in the constructor of the nondefault iterator; Boost’s file-system enumeration component works in this way. 10/26/2009
1 p 510 The STL Iterator concept [Aust1999, Muss2001] is designed to facilitate maximum efficiency of enumeration—usually the only way to “misuse” sequences is to use higher level Iterator concept [Aust1999, Muss2001] behavior, but for_each() requires only input iterators.
The STL Iterator concept [Aust1999, Muss2001] is designed to facilitate maximum efficiency of enumeration—usually the only way to “misuse” sequences is to assume higher level Iterator refinement—e.g. Random Access capabilities in a Forward iterator—but for_each() requires only input iterators. 10/26/2009
1 p 510 int ari[10] = { . . . };

std::for_each(&ari[0], &ari[10], print_int);
int ari[10] = { . . . };

std::for_each(&ari[0], &ari[0] + 10, print_int);
10/26/2009
1 p 510 We also saw that the foolproof way to deal with arrays is to use static array size determination in the form of dimensionof() or a similar construct.
std::for_each(&ari[0], &ari[dimensionof(ari)], print_int);
We also saw that the foolproof way to deal with arrays is to use static array size determination in the form of dimensionof() or a similar construct.
std::for_each(&ari[0], &ari[0] + dimensionof(ari), print_int);
10/26/2009
1 p 510 inline F for_all(T (&ar)[N], F f)
{
return std::for_each(&ar[0], &ar[N], f);
inline F for_all(T (&ar)[N], F f)
{
return std::for_each(&ar[0], &ar[0] + N, f);
10/26/2009
1 p 511 2This is the approach taken by John in his Boost-compatible implementation of RangeLib, although he relies more on the explicit qualification of the RangeLib algorithm namespace boost::rtl::rng::for_each.
2This is the approach taken by John Torjo in his Boost-compatible implementation of RangeLib, although he relies more on the explicit qualification of the RangeLib algorithm namespace boost::rtl::rng::for_each.
10/26/2009
1 p 513 The best way you can handle this is to define the functor class in the compilation unit as the code that’s going to be using it, preferably just before the function where it is used. The best way you can handle this is to define the functor class in the same compilation unit as the code that’s going to be using it, preferably just before the function where it is used. 10/26/2009
1 p 514 Furthermore, Borland and Visual C++ can be tricked into supporting it by the simple rouse of encapsulating the next class within another nested class, as in Furthermore, Borland and Visual C++ can be tricked into supporting them by the simple rouse of encapsulating the class within another nested class, as in 10/26/2009
1 p 514 Table 34.1 summarizes the support for both forms for several popular compilers. Comeau, GCC, and Intel do not supported nested functions in either guise.5 Table 34.1 summarizes the support for both forms for several popular compilers. Comeau, GCC, and Intel do not support nested functions in either guise.5 10/26/2009
1 p 515 The only legal way I know of to make such things work is turgid beyond bearing. Since the template’s parameterizing type must be an external type, we define the function type outside the function. The only legal way I know of to make such things work is turgid beyond bearing. Since the template’s specializing type must be an external type, we define the function type outside the function. 10/26/2009
1 p 520 In fact, when dealing with callback enumeration APIs, local classes are eminently solutions. In fact, when dealing with callback enumeration APIs, local classes are eminently good solutions. 10/26/2009
1 p 521 7John was also a one of the reviewers for this book. 7John was also one of the reviewers for this book. 10/26/2009
1 p 522 There’s noticeable brevity of form, which is part the raison d’être of ranges. A range has the characteristics shown in Table 34.2. There’s noticeable brevity of form, which is part of the raison d’être of ranges. A range has the characteristics shown in Table 34.2. 10/26/2009
1 p 522 Evaluates to true if r has reached its end condition, false otherwise Evaluates to true if r has not reached its end condition, false otherwise 10/26/2009
1 p 526 I must confess that this in itself represents a big attraction to draw for me, but the concept offers much more as we’ll see in the next couple of sections. I must confess that this in itself represents a big attraction for me, but the concept offers much more as we’ll see in the next couple of sections. 10/26/2009
1 p 527 ...to preserve the actual type in the first parameter while at the same type discriminating the overload in the second. ...to preserve the actual type in the first parameter while at the same time discriminating the overload in the second. 10/26/2009
1 p 529 Not only do ranges provide a nice syntactic convenience, but they also facilitate the unified manipulation of iterator-bound ranges and what we might call purely logical ranges. Not only do ranges provide a nice syntactic convenience, but they also facilitate the unified manipulation of iterator-bound ranges and what we might call purely notional ranges. 10/26/2009
1 p 535 Client code cannot change the value of LinkedListCountProp::m_value because it is private, but the friendship declaration means that it can be altered by Rectangle. Client code cannot change the value of LinkedListCountProp::m_value because it is private, but the friendship declaration means that it can be altered by LinkedList. 10/26/2009
1 p 539 Listing 35.8
class LinkedList
{
// Construction
public:
Rectangle(. . .
Listing 35.8
class LinkedList
{
// Construction
public:
LinkedList(. . .
10/26/2009
1 p 540 Nonetheless, it’s worth pointing out, since member ordering dependencies are in principal bad things, and you should be mindful of the pitfalls whenever you (think you) see them. Nonetheless, it’s worth pointing out, since member ordering dependencies are in principal bad things, and you should be mindful of the pitfalls whenever you (think that you) see them. 10/26/2009
1 p 541 However, a quick mental arithmetic will tell us that this will triple (at least) the size of the property. However, a quick mental arithmetic will tell us that this could triple (at least) the size of the property. 10/26/2009
1 p 542 WeekDay get_DayOfWeek() const
{
return WeekDay.m_value;
WeekDay get_DayOfWeek() const
{
return DayOfWeek.m_value;
10/26/2009
1 p 542 , offsetof(Date, DayOfWeek)
. . .
> DayofWeek;
, offsetof(Date, DayOfWeek)
. . .
> DayOfWeek;
10/26/2009
1 p 542 , &WeekDay_offset
. . .
> DayofWeek;
, &WeekDay_offset
. . .
> DayOfWeek;
10/26/2009
1 p 544 method_property_get<WeekDay, WeekDay
, Date
, &DayOfWeek_offset, &get_DayOfWeek
> DayofWeek;
method_property_get<WeekDay, WeekDay
, Date
, &DayOfWeek_offset, &get_DayOfWeek
> DayOfWeek;
10/26/2009
1 p 546 , &C::P##_offset \ , &C::P##_offset##C \ 10/26/2009
1 p 548 , &C::P##_offset \ , &C::P##_offset##C \ 10/26/2009
1 p 552 , &C:: P##_offset##C() \
, &C::P##_offset##C() \
10/26/2009
1 p 561 Listing 35.32
class tm_spy
{
. . .
method_property_getset_external<int, int
, tm_spy
, &sec_offset
, &get_sec
, &set_sec
> tm_sec;
. . .
method_property_getset_external<int, yday
Listing 35.32
class tm_spy
{
. . .
method_property_getset_external<int, int
, tm_spy
, &sec_offset
, &get_sec
, &set_sec
> tm_sec;
. . .
method_property_getset_external<int, int
10/26/2009
1 p 577 [Raym2003] Eric Raymod, The Art of UNIX Programming, Addison-Wesley. [Raym2003] Eric Raymond, The Art of UNIX Programming, Addison-Wesley. 10/26/2009
1 p 579 [Vand2003] Daveed Vandevoorde and Nicolai Josuttis, C++ Templates: The Comprehensive Guide, Addison-Wesley. [Vand2003] Daveed Vandevoorde and Nicolai Josuttis, C++ Templates: The Complete Guide, Addison-Wesley. 10/26/2009
1 p x 20.7  Namespaces and Koenig Lookup 357 20.7  Namespaces and Argument-Dependent Lookup 357 11/10/2009
1 p 94 Assuming sizes of 8, 4, 2, and 1 byte(s) respectively for long, int, short, and char, the size of S will be 15 bytes. However, for a packing alignment of 2 bytes the size will be 16. For an alignment of 4 it will be 24 and for an alignment of 8 or more it will be 32 bytes. Assume sizes of 8, 4, 2, and 1 byte(s) respectively for long, int, short, and char. For a packing alignment of 1 byte, the size of S will be 15 bytes. For an alignment of 2 bytes, the size will be 16 bytes. For an alignment of 4 bytes, the size will be 20 bytes. For an alignment of 8 bytes and 16 bytes, the size will be 24 bytes. 11/10/2009
1 p 139 Given their potential high cost, I tend to use them only for initialization where contention is exceedingly rare, but theoretically possible, and must be accounted for. Given their potential high cost, I tend to use them only for initialization where contention is exceedingly rare, but theoretically possible, and must be accounted for. Also, they are not “reentrant”—locked multiple times—from within the same thread: attempting to do so will cause deadlock. 11/10/2009
1 Globally template constructors/template constructor should be constructor templates/constructor template Affected pages: 221, 223, 309, 331, 335, 351, 362, 386, 390, 391, 392, 518 11/10/2009