Using typedef to Curb Miscreant Code

来源:互联网 发布:学生收费软件 编辑:程序博客网 时间:2024/06/07 06:10

 

Using typedef to Curb Miscreant Code

Typedef declarations help create platform-independent types and can even hide complex or unintelligible syntax. Predictably, however, using typedef comes with its own set of surprises. Learn how to navigate these pitfalls and use typedef to clean up your code.  

 

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[4];size len = file.getlength();std::vector <size> vs; 
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[81];char text[81];
Define a typedef that will be used every time you need an array of the same type and size:
typedef char Line[81];  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.

 


 Code Simplification
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 <csignal> header file reveals signal(), a function which has a similar interface.

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<char, char_traits<char>, allocator<char> > and basic_ofstream<char, char_traits<char> >, respectively.

 

 

Danny Kalev is a system analyst and software engineer with 13 years of experience, specializing in C++ and object-oriented analysis and design. He is a member of the ANSI C++ standardization committee and the author of ANSI/ISO C++ Professional Programmer's Handbook (Que, 1999, ISBN: 0789720221). Reach him at dannykk@inter.net.il.
 
 

原文出处:Using typedef to Curb Miscreant Code

 

译文:http://www.vckbase.com/document/viewdoc/?id=1365

使用 typedef 抑制劣质代码

作者:Danny Kalev
编译:MTT 工作室

原文出处:Using typedef to Curb Miscreant Code

摘要:Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。


 


  typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。

 如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法? 
 
使用 typedefs 为现有类型创建同义字。

定义易于记忆的类型名
  typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:

typedef int size;

  此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:

void measure(size * psz); size array[4];size len = file.getlength();std::vector <size> vs; 

  typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:

char line[81];char text[81];

定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:

typedef char Line[81]; Line text, secondline;getline(text);

同样,可以象下面这样隐藏指针语法:

typedef char * pstr;int mystrcmp(pstr, pstr);

  这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *’类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():

int mystrcmp(const pstr, const pstr); 

  这是错误的,按照顺序,‘const pstr’被解释为‘char * const’(一个指向 char 的常量指针),而不是‘const char *’(指向常量 char 的指针)。这个问题很容易解决:

typedef const char * cpstr; int mystrcmp(cpstr, cpstr); // 现在是正确的

记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。

代码简化
  上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:

typedef int (*PF) (const char *, const char *);

  这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:

PF Register(PF pf);

  Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:

int (*Register (int (*pf)(const char *, const char *))) (const char *, const char *); 

  很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:“OK,有人还会写这样的代码吗?”,快速浏览一下揭示 signal()函数的头文件 <csinal>,一个有同样接口的函数。

typedef 和存储类关键字(storage class specifier)
  这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:

typedef register int FAST_COUNTER; // 错误

  编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。

促进跨平台开发
  typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:

typedef long double REAL; 

在不支持 long double 的机器上,该 typedef 看起来会是下面这样:

typedef double REAL; 

并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样: 、

typedef float REAL; 

  你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string<char, char_traits<char>,allocator<char>> 和 basic_ofstream<char, char_traits<char>>。

 作者简介
  Danny Kalev 是一名通过认证的系统分析师,专攻 C++ 和形式语言理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。 业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究 Hittite、Basque 和 Irish Gaelic 这样的自然语言。其它兴趣包括考古和地理。Danny 时常到一些 C++ 论坛并定期为不同的 C++ 网站和杂志撰写文章。他还在教育机构讲授程序设计语言和应用语言课程。

原创粉丝点击