您的位置:首页 > 编程语言

Mozilla代码风格指南

2008-10-06 23:48 357 查看
注:这是我第一次翻译文章,加上我英语不是很好,这篇翻译的不是很好。感谢我的同学David帮我修正了很多语句不通的地方。
原文: http://www.mozilla.org/hacking/mozilla-style-guide.html

This
document attempts to explain the basic styles and patterns that are
used in the Mozilla codebase. New code should try to conform to these
standards so that it is as easy to maintain as existing code. Of
course every rule has an exception, but it's important to know the
rules nonetheless!

本文档旨在说明Mozilla代码库的基本风格和模式。新代码应遵从本文档的标准,使得新代码和旧代码一样方便维护。当然,任何规则都有例外,但无论如何,了解这些规则还是很重要的。

 

This
is particularly directed at people new to the Mozilla codebase, who
are in the process of getting their code reviewed.
Before getting a review, please read over
this document and make sure your code conforms to the recommendations
here.

本文档特别针对那些正在为Mozilla代码库做代码审查的的新手。在做代码审查之前,务必先阅读本文档,保证你的代码符合本文的建议。

 


General C/C++
Practices


C/C++的一般原则

Have
you checked for compiler warnings?
Warnings often point to real bugs.

 你是否已检查过编译器所报告的warming?
warming通常会指出真正的错误。

Are
the changes 64bit
clean?

是否所有的更改都在64位机上无warning和error?

Do
the changes meet the C++
portability guidelines

是否所有的更改都符合 《c++移植指南》。

Don't
use NULL for pointers. On some systems it's declared as void
*
 and causes a compile warning when assigned to a pointer.
Use 0 or nsnull instead.

不要给指针赋NULL值。
在某些系统中,会被声明为void
*, 并且当赋值给指针时,会引发一个编译warning。因此,应使用0或nsnull来代替。

When
testing a pointer, use !myPtr or (myPtr); don't use myPtr != nsnull
or myPtr == nsnull.

当测试一个指针时,
使用!myPtr或(myPtr),
不要使用myPtr!=nsnull或myPtr==nsnull。

Do
not compare == PR_TRUE or PR_FALSE. Use (x) or (!x) instead. ==
PR_TRUE, in fact, is *different* from if (x)!

不要使用 compare
== PR_TRUE或PR_FALSE。请使用 (x)或(!x)
== PR_TRUE来代替。事实上,这与 if(x)! 是不同的!

Don't
put an else right after a return. Delete the else, it's
unnecessary and increases indentation level.

不要在return的右边写else。删除这个else,这是不必要的并且增加缩进的层次。

Always check
the return value of new for null.

当返回new操作得到的值时,必须检查是否为空。

Don't leave
debug printf()s lying around.

把debug留下的printf()刪掉
(把调试用的printf()语句删掉)

Use JavaDoc-style
comments in
any new class header files.  

在新的类的头文件中使用JavaDoc的注释风格。

When
fixing a problem, check to see if the problem occurs elsewhere in
the same file, and fix it everywhere if possible.

当处理bug时,检查是否该bug在这个文件的其它地方多次出现,并尽量一并修改。

On
whether to use nsFoo aFoo (bFoo) or nsFoo aFoo = bFoo: For
first tier platforms, although the former is theoretically better,
there is probably no reason to prefer it over the latter form. This
is good, since everyone agrees, the form with "=" looks
better. More data for second tier
platforms would be good.

关于使用nsFoo
aFoo(bFOO)还是
nsFoo
aFoo = bFoo的问题:
对于前者,虽然理论上比较好,但是却几乎找不到喜欢它的理由。因为一般人都认为"="号更利于阅读。特别当多个参数时,第二种方式更佳。

Forward
declare classes in your header files instead of including them
whenever possible. For example, if you have an interface with a void
DoSomething(nsIContent* aContent) function, forward declare with
class nsIContent; instead of #include "nsIContent.h"

只要有可能就要在你的头文件里前置声明类,
而不是包含它们。例如, 如果你声明了一个函数接口void
DoSomething(nslContent* aContent), 应前置声明类nslContent,而不是#include
"nslContent.h" 。

 


COM and
pointers


COM和指针

Use
nsCOMPtr<>

If
you don't know how to use it, start looking in the code for
examples. The general rule is that the
very act of typing NS_RELEASE should be a signal to you to question
your code: "Should I be using nsCOMPtr here?". Generally
the only valid use of NS_RELEASE are when you are storing refcounted
pointers in a long-lived datastructure.

使用nsCOMPtr<> 如果你不知道如何使用nsCOMPtr<>,
可以先在代码里查找例子。 
一般规则,输入NS_RELEASE会提醒你思考你写的代  码:"这个地方我需要要用nsCOMPtr吗?"。 通常只有在你需要在生命周期长的数据结构里存储引用计数指针,才需要  使用NS_RELEASE.

Declare
new XPCOM interfaces using XPIDL so
they will be scriptable.

为了使XPCOM接口支持脚本,在使用XPIDL时请用新的XPCOM接口声明。

Use nsCOMPtr for
strong references, and nsWeakPtr for
weak references.

使用nsCOMPtr作为强引用,
而使用nsWeakPtr作为弱引用。

String
arguments to functions should be declared as nsAString.

函数的字符串参数应该声明为nsAString。

Use
str.IsEmpty() instead of str.Length() == 0.

应使用 str.IsEmpty(), 不要使用 str.Length()
== 0 。

Don't
use QueryInterface directly. Use CallQueryInterface or
do_QueryInterface instead.

不要直接使用QueryInterface。而要使用CallQueryInterface 或do_QueryInterface 。

nsresult
should be declared as rv. Not res, not result, not foo.

nsresult变量应声明为rv, 不要声明为res、result或foo 。

For
constant strings, use NS_LITERAL_STRING("...") instead of
NS_ConvertASCIItoUCS2("..."), AssignWithConversion("..."),
EqualsWithConversion("..."), or nsAutoString()

字符串常量应使用 NS_LITERAL_STRING("..."),
不要使用NS_ConvertASClltoUCS2("...")、AssignWithConversion("...")、
EqualsWithConversion("...")、 nsAutoString() 。

Use contractids instead
of progids or class IDs.

使用contractids, 而不要使用progids 或
class
IDs。


IDL


IDL

Use
leading-lowercase, or "interCaps"
 When defining a
method or attribute in IDL, the first letter should be lowercase,
and each following word should be capitalized. For example: long
updateStatusBar();

开头字母小写,
"interCaps" :
在IDL定义一个方法或属性,第一个字母应该小写,紧跟它后面的每个单词的头一个字母应该大写。例如:long updateStatusBar()

Use
attributes wherever possible
 Whenever
you are retrieving or setting a single value without any context, you
should use attributes. Don't use two methods when you could use one
attribute.Using
attributes logically connects the getting and setting of a value, and
makes scripted code look cleaner.


尽可能地使用属性。当你获取或设置一个不需要上下文的单一值时,应使用属性。在你可以使用一个属性时,就不要使用两个方法。理论上,应使用属性来获取或设置值,
这会使脚本代码看起来更简洁。
This
example has too many methods:这是一个使用两个方法的例子:
interface nsIFoo : nsISupports {
    long getLength();
    void setLength(in long length);
    long getColor();
};

   The
code below will generate the exact same C++ signature, but
is more script-friendly.
   下面的代码会生成与c++完全相同的签名,
但它对脚本更友好 。

interface nsIFoo : nsISupports {
    attribute long length;
    readonly attribute long color;
};

Use
java-style constants


使用java风格的常量When
defining scriptable constants
in IDL, the name should be all uppercase, with underscores between
words:
当你在IDL定义脚本常量时,
常量名应全部大写,并且单词之间有下划线:

const long ERROR_UNDEFINED_VARIABLE = 1;


Error handling

错误处理

Check
for errors early and often


要尽早、经常检查错误Every
time you make a call into an XPCOM
function, you should check for an error condition. You need to do
this even if you know that call will never fail. Why?
每次你调用XPCOM函数时,都应检测该函数发生错误的条件。就算你知道了这个函数从不失败,也要这样做。为什么?

Someone
may change the callee in the
future to return a failure condition.

将来可能有人修改被调用的函数,可能导致返回失败的情况。

The
object in question may live on another thread, another process, or
possibly even another machine. The proxy
could have failed to actually make your call in the first place.

问题是,对象可能在另一个线程,
或在另一个进程,甚至有可能在另一台电脑上。实际上,
代理者第一次调用你的函数是有可能失败的。

Use
the nice macros


使用友好的宏

Use
the NS_ENSURE_SUCCESS(rv, rv) and NS_ENSURE_TRUE(expr, rv) macros in
place of if (NS_FAILED(rv)) { return rv; } and if (!expr) { return
rv; }, unless the failure is a normal condition
(i.e. you don't want it to assert).

 应使用 NS_ENSURE_SUCCESS(rv,
rv) 和 NS_ENSURE_TRUE(expr,
rv) 宏,除非失败是一个很经常发生的情况,
你才使用if
(NS_FAILED(rv)) { return rv; } 和 if
(!expr) { return rv; }。(i.e.你不应使用assert来捕捉失败)

Return
from errors immediately


立刻返回错误

In
most cases, your knee-jerk reaction should be to return from the
current function when an error condition occurs. Don't do this:

在大多数情况下,当一个错误情况发生了,你第一反应是应从当前函数返回。不要像这样写:rv = foo->Call1();
if (NS_SUCCEEDED(rv)) {
    rv = foo->Call2();
        if (NS_SUCCEEDED(rv)) {
            rv = foo->Call3();
        }
    }
}
return rv;

 

Instead,
do this:

而应这样写:rv = foo->Call1();
NS_ENSURE_SUCCESS(rv, rv);

rv = foo->Call2();
NS_ENSURE_SUCCESS(rv, rv);

rv = foo->Call3();
NS_ENSURE_SUCCESS(rv, rv);

   

Why?
Because error handling should not obfuscate the logic of the code.
The author's intent in the first example was to make
3 calls in succession, but wrapping
the calls in nested if() statements obscured the most likely behavior
of the code.

为什么?因为错误处理不应把代码的逻辑弄得晦涩难懂。在第一个例子中,作者的意图是在成功时分别调用这3个函数,但把函数的调用封装在嵌套的if()语句里,使代码看起来很晦涩难懂。

 

 

Consider
a more complicated example that actually hides a bug:

考虑一个比较复杂的例子,其实它隐藏一个臭虫(bug):
PRBool val;
rv = foo->GetBooleanValue(&val);
if (NS_SUCCEEDED(rv) && val)
  foo->Call1();
else foo->Call2();

 

The
intent of the author may have been that foo->Call2() would only
happen when val had a false value. In fact, foo->Call2() will also
be called when foo->GetBooleanValue(&val) fails. This may or
may not have been the author's intent, and it is not clear from this
code. Here is an updated version:

作者的意图是,当val的值为false时,foo->Call2()才会调用。实际上,当foo->GetBooleanValue(&val)调用失败时, foo2->Call2() 同样也会被调用。这可能是,也有可能不是作者的意图,这段代码表述不清。这里是一个升级版本:
PRBool val;
rv = foo->GetBooleanValue(&val);
if (NS_FAILED(rv)) return rv;
if (val)
   foo->Call1();
else
   foo->Call2();

 

In
this example, the author's intent is clear, and
an error condition avoids both calls to foo->Call1() and
foo->Call2();

Possible
exceptions:
 Sometimes it is not fatal if a call fails. For
instance, if you are notifying a series of
observers that an event has fired, it might be inconsequential that
one of these notifications failed:

在这个例子了,作者的意图很清晰,在错误发生情况下避免同时调用foo->Call1() 和foo->Call2(); 可能的例外:有时候函数调用失败不是致命的。比如,当一个事件被触发要通知一系列观察者,其中一个事件通知失败是无关紧要的。
for (i=0; i<length; i++) {
        // we don't care if any individual observer fails
        observers->Observe(foo, bar, baz);
    }

 

Another
possibility is that you are not sure if a component exists or is
installed, and you wish to continue normally if the component is not
found.

另一个可能的是你无法肯定组件是否存在或已安装,而你希望就算找不到该组件,也正常运行下去。
nsCOMPtr<nsIMyService> service = do_CreateInstance(NS_MYSERVICE_CID, &rv);
// if the service is installed, then we'll use it
if (NS_SUCCEEDED(rv)) {
    // non-fatal if this fails too, ignore this error
    service->DoSomething();

    // this is important, handle this error!
    rv = service->DoSomethingImportant();
    if (NS_FAILED(rv)) return rv;
}
    
// continue normally whether or not the service exists


Strings


字符串

Use
the Auto form of strings for local values


使用自动形式的局部字符串变量

 

When
declaring a local, short-lived nsString class, always use
nsAutoString or nsCAutoString - these versions pre-allocate a 64-byte
buffer on the stack, and avoid fragmenting the heap. Don't do this:

在声明一个局部、生命周期短的nsString类时,总是使用nsAutoString或nsCAutoString------这些版本的字符串类会在堆栈上预分配64字节的内存,避免内存碎片。不要这样写:
nsresult foo() {
  nsCString bar;
  ..
}

 

instead:

取而代之:
nsresult foo() {
  nsCAutoString bar;
  ..
}

Be
wary of leaking values from non-XPCOM functions that return char* or
PRUnichar*


小心non-XPCOM 函数因返回char* PRUnichar*而产生内存泄漏

 

It
is an easy trap to return an allocated string from an internal helper
function, and then use that function inline in your code without
freeing the value. Consider this code:

在你的代码使用一个会返回分配内存的字符串的内部辅助函数,但不释放内存,这是一个很明显的自陷。考虑下面的代码:
static char *GetStringValue() {
    ..
    return resultString.ToNewCString();
}

    ..
    WarnUser(GetStringValue());

 

In
the above example, WarnUser will get the
string allocated from resultString.ToNewCString() and throw
away the pointer. The resulting value is never freed. Instead, either
use the string classes to make sure your string is automatically
freed when it goes out of scope, or make sure that your string is
freed.

在上面的例子,把已分配内存的resultString赋值给WamUser。ToNewCString()会扔掉指针。但返回值从未被释放。因此,要么使用自动形式的字符串类,确保当你的字符串超出作用域时会被释放,要么确保你的字符串已被释放。

 

 

Automatic
cleanup:

自动清除:
static void GetStringValue(nsAWritableCString& aResult) {
    ..
    aResult.Assign("resulting string");
}

    ..
    nsCAutoString warning;
    GetStringValue(warning);
    WarnUser(warning.get());

 

Free
the string manually:

手动释放字符串:

 
static char *GetStringValue() {
    ..
    return resultString.ToNewCString();
}

    ..
    char *warning = GetStringValue();
    WarnUser(warning);
    nsMemory::Free(warning);

 

Use
NS_LITERAL_STRING() to avoid runtime string conversion
.

使用 NS_LITERAL_STRING() 来避免运行时字符串转换
It
is very common to need to assign the value of a
literal string such as "Some String" into a
unicode buffer. Instead of using nsString's AssignWithConversion and
AppendWithConversion, use NS_LITERAL_STRING() instead. On most
platforms, this will force the compiler to
compile in a raw unicode string, and assign it directly.转换常量字符串是一件很普遍的事,
例如“Some
String”,转换成unicode内存
。使用NS_LITERAL_STRING() 来取代nsString的AssignWithConversion 和 AppendWithConversion。在大多数平台上,它会强制编译器把它编译成一个unicode字符串,然后直接赋值给它。

 

Incorrect:

不正确的:
nsAutoString warning; warning.AssignWithConversion("danger will robinson!");
..
foo->SetUnicodeValue(warning.get());

Correct:

正确的:NS_NAMED_LITERAL_STRING(warning,"danger will robinson!");
..
// if you'll be using the 'warning' string, you can still use it as before:
foo->SetUnicodeValue(warning.get());

// alternatively, use the wide string directly:
foo->SetUnicodeValue(NS_LITERAL_STRING("danger will robinson!").get());

 


Naming and
Formatting code


变量命名和代码风格


Note: the
following is not all set in stone, this is
interim to give people a chance to look


注意:下面规则不全是硬性规定,只是临时让开发者有机会了解

[i]Use
the prevailing style in a file or module, or
ask the owner, if you are on someone else's turf. Module
owner rules all.


在文件或模块中使用普遍的代码风格,或者如果你在某些人的领导下,可询问项目主管。
模块拥有者规定代码风格。

Whitespace: No
tabs. No whitespace at the end of a line.

空白:不要用tabs键。每一行的末尾没有空白。

Line
Length:
 80 characters or less (for Bonsai and printing).

行长:
不要多于80个字符(方便Bonsai查看和打印)。

Control
Structures:


控制结构:
if (...) {
} else if (...) {
} else {
}

while (...) {
}

do {
} while (...);

for (...; ...; ...) {
}

switch (...)
{
  case 1:
    {
      // When you need to declare a variable in a switch, put the block in braces
      int var;
    } break;
  case 2:
    ...
    break;
  default:
    break;
}

Classes:

类:

class nsMyClass : public X,
                       public Y
{
public:
  nsMyClass() : mVar(0) { ... };
  
private:
  int mVar;
};

Methods:

方法:

int
nsMyClass::Method(...)
{
  ...
}

Mode
Line:
 Files should have an Emacs mode line comment
as the first line of the file, which should set indent-tabs-mode to
nil. For new files, use this, specifying 2-space indentation:

  :
文件都应该有一行Emacs
mode line注释作为文件的第一行,它会设置indent-tabs-mode为nil。在新文件中使用2个空格作为缩进。

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

Operators should
be at the end of a line, not the beginning of the next, if an
operator is at a line break.

操作符:
如果一个操作符在换行地方,该操作符应在行的末尾,而不是在下一行的开头。

Follow
naming prefix conventions.

遵守以下前缀命名的约定

 

Variable
prefixes:


变量前缀

k=constant
(e.g. kNC_child)

k=常量(如,kNC_child)

g=global
(e.g. GprefService)

g=全局(如,GperfService)

m=member
(e.g. Mlength)

m=成员(如,Mlength)

a=argument
(e.g. Acount)

a=参数(如,Acount)

s=static
member (e.g. sPrefChecked) 

s=静态成员(如,sPrefChecked)

Global
functions/macros/etc


全局函数/宏/等其他

Macros
begin with NS_, and are all caps (e.g.
NS_IMPL_ISUPPORTS)

宏以NS_开头,所有字母都大写(例,NS_IMPL_ISUPPORTS)

Global
(exported) functions begin with NS_ and
use LeadingCaps (e.g. NS_NewISupportsArray)

全局(导出)函数以NS_开头,以后的每个单词都以大写字母开头(例,NS_NewISupportsArray)

 

 

Original
document by Alec
Flett.

由Alec
Flett撰写原稿。

 

 

Thanks
to:

感谢:

pink

smfr

waterson

jband

brendan

rogc

for
additional comments.

进一步评论。

 

 

Additions
by Akkana
Peck based
on discussions on IRC: thanks to: bbaetz, bz, jfrancis, jkeiser,
mjudge, and sdagley for comments, and to John
Keiser and JST's Reviewer Simulacrumand Brendan
and Mitchell's super-review document.

由Akkana
Peck 基于在IRC上的讨论添加:感谢:bbaetz,
bz, jfrancis, jkeiser, mjudge, 和 sdagley的评论,以及提到的John
Keiser and JST's Reviewer Simulacrum 和 Brendan
and Mitchell's super-review document。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: