Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Prologue: Philosophy of the Imperfect Practitioner


Terminology

Since computers speak the exact language of machine code, and human beings speak the various inexact languages of mankind, I'd like to define a few terms here that will be used throughout the remainder of the book:

Client code. This is code that uses other code, usually, but not limited to, the situation of application code using library code.

Compilation unit. The combination of all source from a source file and its entire dependent include files.

Compile environment. The combination of compiler, libraries, and operating system against which a given code set is compiled. Thanks to Kernighan and Pike [Kern1999] for this one.

Functor. This is a widely used term for Function Object or Functional, but it's not in the standard. I actually prefer Function Object, but I've been persuaded[4] that Functor is better because it's one short word, is more distinctive, and, most significantly, is more searchable, especially when searching online.

[4] Blame several of my reviewers for this.

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.

In addition to these conceptual terms, I'm also including some specific language-related terms. I don't know about you, but I find the nomenclatural clutter in C++ more than a little confusing, so I'm going to take a small time out to drop in some definitions. The following are derived from the standard, but presented in a simpler manner for all our understanding, not least my own. Several of these definitions overlap, since they address different concepts, but all form part of the vocabulary of the accomplished C++ practitioner, imperfect or not.

Fundamental Types and Compound Types

The fundamental types (C++-98: 3.9.1) are the integral types (char, short, int, long (long long / __int64), the (signed and) unsigned versions thereof,[5] and bool), the floating-point types (float, double, and long double) and the void type.

[5] Note that char comes in three flavors: char, unsigned char, and signed char. We see how this can be problematic in Chapter 13.

Compound types (C++-98: 3.9.2) are pretty much everything else: arrays, functions, pointers (of any kind, including pointers to nonstatic members), references, classes, unions, and enumerations.

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.

Object Types

Object types (C++-98: 3.9; 9) include any type that is "not a function type, not a reference type, and not a void type." This is another term I avoid, since it does not mean "instances of class types" which one might think. I use instances to refer to such things throughout the book.

Scalar Types and Class Types

Scalar types include the (C++-98: 3.9; 10) "arithmetic types, enumeration types [and] pointer types." Class types (C++-98: 9) are those things declared with one of the three class-keys: class, struct or union.

A structure is a class type defined with the class-key struct; its members and base classes are public by default. A union is a class type defined with the class-key union; its members are public by default. A class is a class type defined with the class-key class; its members and base classes are private by default.

Aggregates

The standard (C++-98: 8.5.1; 1) describes an aggregate as "an array or a class with no user-defined constructors, no private or protected non-static data members, no base classes, and no virtual functions." As an array or a class type, it means that there is a bringing together of several things into one, hence aggregate.

Aggregates may be initialized via a brace enclosed initializer clause, as in:



struct X


{


 int i;


 short as[2];


} x = { 10, { 20, 30 }};



Although aggregates are usually comprised of POD types (see the next section), they do not have to be. X's member variable i could be of a class type, so long as it had a non-explicit constructor (see section 2.2.7) taking a single integer argument and an available copy constructor.

POD Types

POD, which stands for plain-old-data (C++-98: 1.8; 5), is a very important concept in C++, but often misunderstood, and not least by this little pixie. It's also quite poorly defined. The standard gives two clues. In (C++-98: 3.9; 2) we are told that "for any complete POD type T . . . the bytes making up the object can be copied into an array of [bytes]. If the content of the array . . . is copied back . . . the object shall hold its original value." In (C++-98: 3.9; 3) we are further informed that "for any POD type, if two pointers to T point to distinct T objects obj1 and obj2, if the value of obj1 is copied into obj2, using the memcpy() function, obj2 shall subsequently hold the same value as obj1."

Wow! That's pretty slick, eh? Thankfully it does offer us a few—fifty-six to be precise—more crumbs dotted throughout its 776 pages in order to flesh out this definition.

In [Como-POD], Greg Comeau comments that most books (on C++) don't mention POD at all, and posits "most books are not worth buying." In a cynical attempt to improve saleability, I'm going to do my best here. It shouldn't be too hard, because in the same document, from which I've generously lifted here, Greg provides all the essential attributes of a POD-struct. Let's dig in.

The standard (C++-98: 3.9; 10) collectively defines POD types as "scalar types, POD-struct types, POD-union types, arrays of such types and [const and/or volatile] qualified versions of such types." That's all pretty clear, apart from POD-struct and POD-union.

A POD-struct is (C++-98: 9;4) "an aggregate class that has no non-static data members of type pointer to member, non-POD-struct, non-POD-union, or arrays of such types, or reference, and has no user-defined copy assignment operator and no user-defined destructor." A POD-union has the same definition, except it is a union rather than a struct. Note that an aggregate class can use either the class-key struct or the class-key class.

So far so good, but what's the point of POD? Well, POD types allow us to interact with C functions: they're the common currency between C++ and C, and therefore, by extension, between C++ and the outside world (see Chapters 79). Hence a POD-struct or union allows us to "obtain what a normal struct [or union] would look like and be in C" [Como-POD]. While you should certainly be aware of the various aspects of POD, the best short mnemonic for POD is a type compatible with C.

Other important aspects of POD are

  • The type for which the macro offsetof() is defined shall be "a POD structure or a POD union" (C++-98: 18.1; 5). The use of any other type (see section 2.3.2 and Chapter 35) is undefined.

  • A POD can be put in a union. This is used to constrain POD types (see section 1.2.4).

  • A static object of POD type initialized with constant expressions is initialized before its block is entered, and before any objects (POD or otherwise) requiring dynamic initialization (see Chapter 11).

  • Pointers to members are not POD types, as opposed to all other pointer types.

  • POD-struct or POD-union types can have static members, member typedefs, nested types, and methods.[6]

    [6] Naturally, none of these aspects may be visible to any C code, and must be elided from any C/C++ compatible code via conditional compilation.


      Previous section   Next section