typedef declaration, or a typedef for short, creates a new name for an existing type. As such, it is often used in writing more aesthetic and readable code. Aesthetics not withstanding, typedefs can also hide unwieldy syntactic constructs and platform-dependent datatypes, thereby enhancing portability and future maintenance. The following sections show how to exert the power of typedef while avoiding common traps.
How to create platform-independent datatypes and hide cumbersome, if not unintelligible, syntax?
Use typedefs to create synonyms for existing types.
Defining Mnemonic Type Names
The most common use of typedefs is creating mnemonic type names that document the programmer’s intention. The type being declared appears in the position of a variable’s name, right after the keyword ‘typedef’. For example,
typedef int size;
This declaration defines a synonym for int called size. Notice that a typedef doesn’t create a new type; it merely adds a synonym for some existing type. You can use size in any context that requires int:
void measure(size * psz); size array;size len = file.getlength();std::vector
typedefs may also disguise composite types such as pointers and arrays. For example, instead of repeatedly declaring an array of 81 characters like this:
char line;char text;
Define a typedef that will be used every time you need an array of the same type and size:
typedef char Line; Line text, secondline;getline(text);
Similarly, hide pointer syntax like this:
typedef char * pstr;int mystrcmp(pstr, pstr);
This brings us to the first typedef trap. The standard function strcmp() takes two arguments of type ‘const char *’. Therefore, it might be tempting to declare mystrcmp() like this:
int mystrcmp(const pstr, const pstr);
This is wrong, though. The sequence ‘const pstr’ is interpreted as ‘char * const’ (a const pointer to char), rather than ‘const char *’ (a pointer to const char). You can easily solve this problem, though:
typedef const char * cpstr; int mystrcmp(cpstr, cpstr); //now correct
Remember: Whenever you declare a typedef for a pointer, adding const to the resulting typedef name makes the pointer itself const, not the object.
The typedefs I’ve shown thus far behave like a #define macro that substitutes a synonym with its actual type. Yet unlike macros, typedefs are interpreted at compile-time, thereby enabling the compiler to cope with textual substitutions that are beyond the preprocessor’s capabilities. For example,
typedef int (*PF) (const char *, const char *);
This declaration introduces the type PF as a synonym for ‘pointer to function taking two const char * arguments and returning int’. In the following function declaration, the use of this typedef is indispensable:
PF Register(PF pf);
Register() takes a callback function of type PF and returns the address of a function with a similar signature that was previously registered. Take a deep breath. I’m about to show you how this declaration would look without a typedef:
int (*Register (int (*pf)(const char *, const char *))) (const char *, const char *);
Few programmers understand what it means, not to mention its risk of introducing mistakes into such convoluted code. Obviously, the use of a typedef here isn’t a prerogative but a must. “OK, but does anyone write such code anyway?” the skeptics among you are probably asking. A quick glance at the
typedef and Storage Specifiers
Surprising as it may sound, typedef is a storage class specifier, just like auto, extern, mutable, static and register. This doesn’t mean that typedef actually affects the storage characteristics of an object; it only means that syntactically, typedef declarations look like declarations of static, extern etc., variables. This brings us to trap #2:
typedef register int FAST_COUNTER; //error
This won’t compile. The problem is that you can’t have multiple storage class specifiers in a declaration. Because the token typedef already occupies the position of a storage class specifier, you can’t use register (or any other storage class specifier) in a typedef declaration.
Facilitating Cross-platform Development
typedefs have another important use, namely defining machine-independent types. For example, you can define a floating point type called REAL that has the highest precision available on the target machine:
typedef long double REAL;
On machines that don’t support long double, this typedef will look like this:
typedef double REAL;
And on machines that don’t even support double:
typedef float REAL;
You can compile applications that use the type REAL on every platform without making any changes to the source file. The only thing that will change is the typedef itself. In most cases, even this tiny change will be totally automatic thanks to the wonders of conditional compilation. Nifty, isn’t it? The Standard Library uses typedefs extensively to create such platform-independent types: size_t, ptrdiff_t and fpos_t are a few examples. Likewise, typedefs such as std::string and std::ofstream hide the long and nearly unintelligible template specializations basic_string