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++的一般原则
Are the changes 64bit clean?
Do the changes meet the C++ portability guidelines
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.
Use JavaDoc-style comments in any new class header files.
When fixing a problem, check to see if the problem occurs elsewhere in the same file, and fix it everywhere if possible.
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和指针
-
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<>, 可以先在代码里查找例子。 一般规则,输入NS_RELEASE会提醒你思考你写的代 码:"这个地方我需要要用nsCOMPtr吗?"。 通常只有在你需要在生命周期长的数据结构里存储引用计数指针,才需要 使用NS_RELEASE.
Declare new XPCOM interfaces using XPIDL so they will be scriptable.
Don't use QueryInterface directly. Use CallQueryInterface or do_QueryInterface instead.
不要直接使用QueryInterface。而要使用CallQueryInterface 或do_QueryInterface 。
For constant strings, use NS_LITERAL_STRING("...") instead of NS_ConvertASCIItoUCS2("..."), AssignWithConversion("..."), EqualsWithConversion("..."), or nsAutoString()
字符串常量应使用 NS_LITERAL_STRING("..."), 不要使用NS_ConvertASClltoUCS2("...")、AssignWithConversion("...")、 EqualsWithConversion("...")、 nsAutoString() 。
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()
The code below will generate the exact same C++ signature, but is more script-friendly.
- interface nsIFoo : nsISupports {
- long getLength();
- void setLength(in long length);
- long getColor();
- };
下面的代码会生成与c++完全相同的签名, 但它对脚本更友好 。
- interface nsIFoo : nsISupports {
- attribute long length;
- readonly attribute long color;
- };
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
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函数时,都应检测该函数发生错误的条件。就算你知道了这个函数从不失败,也要这样做。为什么?
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来捕捉失败)
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;
- 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:
- 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[i]->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
字符串
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;
- ..
- }
- nsresult foo() {
- nsCAutoString bar;
- ..
- }
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()会扔掉指针。但返回值从未被释放。因此,要么使用自动形式的字符串类,确保当你的字符串超出作用域时会被释放,要么确保你的字符串已被释放。
- static void GetStringValue(nsAWritableCString& aResult) {
- ..
- aResult.Assign("resulting string");
- }
- ..
- nsCAutoString warning;
- GetStringValue(warning);
- WarnUser(warning.get());
- static char *GetStringValue() {
- ..
- return resultString.ToNewCString();
- }
- ..
- char *warning = GetStringValue();
- WarnUser(warning);
- nsMemory::Free(warning);
使用 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字符串,然后直接赋值给它。
- nsAutoString warning; warning.AssignWithConversion("danger will robinson!");
- ..
- foo->SetUnicodeValue(warning.get());
- 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
注意:下面规则不全是硬性规定,只是临时让开发者有机会了解
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.
- 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;
- }
- class nsMyClass : public X,
- public Y
- {
- public:
- nsMyClass() : mVar(0) { ... };
- private:
- int mVar;
- };
- 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.
Original document by Alec Flett.
由Alec Flett撰写原稿。
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。