podofo-0.9.0 文档
第1章
主页
PdfCompilerCompat.h 汇集了不受欢迎的各种编译器兼容性需求到一个集中的位置。如果所有的编译器特定的定义、封装程序和喜好对库的公共用户是必须可见的,那么它们都应该被包含在这儿和PdfCompilerCompat.cpp(如果必要)中。
如果那些令人讨厌的平台和编译器的特定技巧可以被保持到PoDoFo的构建中和对库的用户不可见的话,可将它们放在PdfCompilerCompatPrivate.{cpp,h} 之中。
请永远不要在一个"using"指令中使用来自于这个头文件或者PoDoFo::compat名称空间的符号。总是显示地引用名称以使得当你从繁琐的兼容中把它们拉出来用时它们是清晰的。
第2章
(代码样式)
这个文档试着给出在PoDoFo中使用的代码样式的一个概述。为了保持代码规范,每个提交都应该支持这个代码样式。
PoDoFo的代码样式绝不是完美的,在某些方面甚至不是维护人员的首选代码样式。但一致性是比个人喜好更为重要的,这个样式的大多数部分可以通过简单的编辑器设置来应用。
2006 Dominik Seichter
1. 总体规则
===============
文档是PoDoFo的一个重要部分。在Doxygen格式中,每个类和方法应该包括适当的文档以让自动API文档能够被生成且代码能够被每个人很容易地理解。
头文件中的注释是非常重要的,即使在源代码本身中可能包含其他的信息如果必要的话。
2. 代码格式化
==================
2.1 缩进
代码由4个空格来缩进。没有标签,没有讨论 ;)
2.2 括弧
括弧总是在它们自己的一个新行开始。唯一的例外是类和结构体的声明和try与catch块。
示例:
if( true )
{
}
但是
class MyClass{
};
如果在一个if或while语句后仅有一行,则不需要括弧,然而它们总是被使用如果作者感觉在这个区域有将来要扩展的可能。
2.3 内联函数
内联函数被声明在头文件的类定义中。它们在头文件的类定义后被实现。
作者可以选择直接在声明的同一位置实现它们或把它们放置在头文件的最后(首选)。
3. 命名
=========
3.1 变量
有人在PoDoFo中开始使用匈牙利命名法。好吧,维护者认为这是他有过的最糟糕的主意... 就算如此,关键是一致性和没有个人偏好。
PoDoFo为以下类型使用匈牙利命名法:
enum 类型名 应该以一个E开始
enum 变量 应该以一个e开始
结构体 类型名 应该以一个T开始
结构体 变量名 应该以一个t开始
指针 应该以一个p开始
字符串 应该以一个s开始
c-字符串 应该以psz(以0结尾的指针)开始
数字 应该以一个n开始
long类型 应该以一个l开始
bool类型 应该以一个b开始
引用 常以一个r开始
示例:
bool bDecision;
long lValue;
char* pszString;
int nNumber;
3.2 成员变量
类中的成员变量附加前缀"m_"。
示例:
class MyClass {
private:
bool m_bMemberVar;
};
3.3 方法
所有的方法都已一个大写字母开始,每个新单词都再次首字母大写。
MyClass::FunctionWithLongName( long lParameter );
属性使用一个带"Set"前缀的函数来设置,而获取属性则带一个"Get"。另外,除非有一个好的理由不这样做——则所有的"Getters"都应该被标记为常量。
MyClass::SetProperty( long lValue );
long MyClass::GetProperty() const;
此外,请在适当的时候使用"Has" 和 "Is"前缀。例如:
PdfDictionary::HasKey();
PdfDocument::IsLinearized();
避免throw() 限定符 (参考3.5)。
3.4 NULL-指针
NULL 指针在代码中用常亮NULL来初始化。请不要用0 或 0L 而是用 NULL。
4. 通用准则
===================
4.1 转换
C++ 样式转换是强烈的首选,C风格样式的使用将在gcc编译时产生警告。使用适当的,
static_cast<>
const_cast<>
reinterpret_cast<>
动态转换和typeid 在PoDoFo中目前不被使用。
const_cast<> 应该要避免使用除非它是绝对需要的,尤其是对‘const char *’变量来说可能只得到一个字符串字面值。
4.2 局部变量声明
局部变量应该总是在最接近使用它们的地方被声明,应该尽可能声明为‘const’。
例如:
Thing f()
{
Thing ret;
// 废话 废话 废话
ret = DoSomething();
// 废话 废话 废话
return ret;
}
最好写成:
Thing f()
{
// 废话 废话 废话
Thing ret ( DoSomething() );
//废话 废话 废话
return ret;
}
记住你的常量指针:
char * x; 指向字符的指针
const char * x; 指向常量字符的指针
char * const x; 指向字符的常指针
const char * const x; 指向常量字符的常指针
4.3 静态数组
静态数据应该尽可能地声明为一个常量字符数组而不是一个指向常量字符的指针。这将帮助编译器把它们放在编译对象的静态只读数据区域,以致使一个更小的内存占用、更快的加载时间和防止意外写数据的硬件保护。
const char myStaticData[] = "This is the right way";
const char * myStaticData = "Avoid this way";
二维数组可以用类似的方法指定——参考PdfDefines.{cpp,h}中的s_szPdfVersions。耗费几个字节来填充最长成员的数组长度并让它们成为可执行文件的只读数据部分比用一个指向字符的指针数组以挽救几个字节较为更好。然而哪个是最好的,则依赖于给定情况下的"几个字节"。
4.4 临时对象的使用
在可能的情况下,使用一个临时的比存储一个命名的对象更好。例如:
DoSomething( PdfName("blah") );
而不是
PdfName n("blah");
DoSomething( n );
这样做可以使编译器的调用优化更为容易、可以减少函数的堆栈大小,等等。然而,不要忘了考虑临时对象的生命周期。
4.5 ‘throw’ 限定符
在任何情况下都不适用异常限定符,甚至是空的异常限定符‘throw()’。C++检查异常——当根据标准实现的时候——本质上是无用的且实际上可能是昂贵的。如果你想告诉编译器一个方法将不会throw(作为一个优化)的话,则用declspec(nothrow) 来代替。podofoapi 提供了适当的宏来在podofo中使用。(注意VC++违反标准地把throw()作为 declspec(nothrow)来处理,但那就更有理由要使用declspec(nothrow))
参考:
http://msdn2.microsoft.com/en-us/library/49147z04.aspx
http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Function-Attributes.html
4.6 导出API
PoDoFo描绘了一些平台上导出的和私有的API间的差别(目前Windows DLL 编译和gcc 4用PODOFO_USE_VISIBILITY)。为了这样做它用podofoapi.h中的一些宏定义来告诉编译器哪些公共的API应该出现在DLL/shared库的符号列表中。其余的不导出。
这可能有一些积极作用,它取决于特定的平台和编译器。它可能导致更小的二进制、更好的链接时间、帮助编译器优化得更好和确保对PoDoFo来说打算是私有的API不会从它的外面被调用。
如果你添加新类到PoDoFo中,那么用PODOFO_API像podofoapi.h中所展示的那样注解它们如果它们将打算作为公共API的话。如果一个外面的用户需要直接地引用(用来构造类、调用它们的方法等等)这些符号,那么它们就是公共的。
注意那些仅继承和实现一个只通过工厂或其他PoDoFo类来构造的抽象接口(不打算添加其他的公共方法给PoDoFo外部所用)是不需要导出的。
如果你有一个需要作为公共API导出的类,但它有相当多的方法无需对外可见的话(私有辅助方法等待),你可以用PODOFO_LOCAL宏像podofoapi.h中所展示的那样去标注它们。这仅仅是从符号表中省略了这些方法。注意如果方法经由公共或保护的内联函数被访问,则设置它们为私有的是不安全的。
如果有疑问,可向podofo-users寻求帮助。它也有助于构建PoDoFo为一个DLL(Windows),或在UNIX平台上使用gcc4和启用可视化支持。这将有助于在你忘记导出所需API的地方捕获情况。
4.7 内联函数中的内存分配
在内联函数中(直接)分配或释放堆内存是不安全的,因为只当同一运行时库被用在共享库中和可执行文件链接到库中时才工作。在MS Windows系统上内联函数中使用malloc 和/或 new 将导致崩溃。这在Linux系统上可能是不被发现的(尽管在Linux上也是不好的样式),因为在Linux系统上大多数的进程和库都使用同一个运行时库。
调用了new/delete/malloc/free的内联函数也是没有意义的,因为内存分配的成本显然要高于一个纯粹的函数调用。
使用STL类 执行内部的分配可能是不错的因为它们倾向于使用它们自己的在它们附近的std::allocator实例(或引用,不管怎样)。
4.8 第3方头文件的可见性
如果有可能的话,在PoDoFo头文件中不暴露第3方头文件是合适的。不是为所需的库包含头文件,而应试着前置声明所需的类型然后在实现文件(.cpp)中包含头文件。如果头文件被广泛使用,那么你可能想要把它们放在PdfDefinesPrivate.h中。广泛使用的前置声明可以放在Pdf3rdPtyForwardDecl.h 中。
避免暴露使用第3方头文件意味着用户的编译系统不需要知道怎样找到这些头文件,和意味着用户的程序不需要获得它们的被来自于libjpeg、zlib、libtiff、freetype等等等等的不相关的符号所污染的名称空间。像一些头文件(freetype)简单的可靠定位,它们这真正地可以简化使用PoDoFo的构建工具。
这样适用于一些系统头文件。例如
如果你需要包含一个第3方头文件来让某些东西称为直接的成员,考虑使其通过指针来代替它成为一个成员,在对象的构造函数中初始化并在析构函数中销毁它。在PoDoFo头文件中你不需要包含第3方头文件来获得它们的类型大小的方式仅在.cpp文件中。例如,参考PdfMutex.h。
5. 结构
============
5.1 项目结构
PoDoFo项目结构如下。
有两个库:podofo-base和podofo-doc。Podofo-base包含需要与读、写和修改PDF文件一同工作的所有东西和它们的对象。它应该有一个最小化地址依赖设置。Podofo-doc 提供了一个丰富的接口,它也允许用PdfPainter和PdfFont基础结构来轻松地创建PDF文件。
此外,有两个更多的项目。test/ 子目录包含了两个库的测试。所有的新测试将在test/unit/子项目为PoDoFo提供单元测试。伴随PoDoFo的实用程序在tools/ 子目录。这些工具给那些想在命令行里与PDF文件一同工作的终端用户提供了直接的便利,也是给新的开发人员展示PoDoFo库特点的一种很好的方式。