Mozilla代码风格指南

注:这是我第一次翻译文章,加上我英语不是很好,这篇翻译的不是很好。感谢我的同学David帮我修正了很多语句不通的地方。



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位机上无warningerror

  • 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。因此,应使用0nsnull来代替。

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

  • 当测试一个指针时, 使用!myPtr(myPtr), 不要使用myPtr!=nsnullmyPtr==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_TRUEPR_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, 不要声明为resresultfoo 

  • 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定义一个方法或属性,第一个字母应该小写,紧跟它后面的每个单词的头一个字母应该大写。例如:

    1. 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:

    这是一个使用两个方法的例子:

  1. interface nsIFoo : nsISupports {
  2.     long getLength();
  3.     void setLength(in long length);
  4.     long getColor();
  5. };
   The code below will generate the exact same C++ signaturebut is more script-friendly.
   下面的代码会生成与c++完全相同的签名, 但它对脚本更友好

  1. interface nsIFoo : nsISupports {
  2.     attribute long length;
  3.     readonly attribute long color;
  4. };
  • Use java-style constants

  • 使用java风格的常量

    When defining scriptable constants in IDL, the name should be all uppercase, with underscores between words:

    当你在IDL定义脚本常量时, 常量名应全部大写,且单词之间有下划线:

  1. 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:

在大多数情况下,当一个错误情况发生了,你第一反应是应从当前函数返回。不要像这样写

  1. rv = foo->Call1();
  2. if (NS_SUCCEEDED(rv)) {
  3.     rv = foo->Call2();
  4.         if (NS_SUCCEEDED(rv)) {
  5.             rv = foo->Call3();
  6.         }
  7.     }
  8. }
  9. return rv;

 

Instead, do this:

应这样

  1. rv = foo->Call1();
  2. NS_ENSURE_SUCCESS(rv, rv);
  3. rv = foo->Call2();
  4. NS_ENSURE_SUCCESS(rv, rv);
  5. rv = foo->Call3();
  6. 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 successionbut 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)

  1. PRBool val;
  2. rv = foo->GetBooleanValue(&val);
  3. if (NS_SUCCEEDED(rv) && val)
  4.   foo->Call1();
  5. 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() 同样也会被调用。这可能是,也有可能不是作者的意图,这段代码表述不清。这里是一个升级版本:

  1. PRBool val;
  2. rv = foo->GetBooleanValue(&val);
  3. if (NS_FAILED(rv)) return rv;
  4. if (val)
  5.    foo->Call1();
  6. else
  7.    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(); 可能的例外:有时候函数调用失败不是致命的。比如,当一个事件被触发要通知一系列观察者,其中一个事件通知失败是无关紧要的。

  1. for (i=0; i<length; i++) {
  2.         // we don't care if any individual observer fails
  3.         observers[i]->Observe(foo, bar, baz);
  4.     }

 

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.

另一个可能的是你无法肯定组件是否存在或已安装,而你希望就算找不到该组件,也正常运行下去。

  1. nsCOMPtr<nsIMyService> service = do_CreateInstance(NS_MYSERVICE_CID, &rv);
  2. // if the service is installed, then we'll use it
  3. if (NS_SUCCEEDED(rv)) {
  4.     // non-fatal if this fails too, ignore this error
  5.     service->DoSomething();
  6.     // this is important, handle this error!
  7.     rv = service->DoSomethingImportant();
  8.     if (NS_FAILED(rv)) return rv;
  9. }
  10.     
  11. // 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类时,总是使用nsAutoStringnsCAutoString------这些版本的字符串类会在堆栈上预分配64字节的内存,避免内存碎片。不要这样

  1. nsresult foo() {
  2.   nsCString bar;
  3.   ..
  4. }

 

instead:

取而代之

  1. nsresult foo() {
  2.   nsCAutoString bar;
  3.   ..
  4. }
  • 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:

在你的代码使用一个会返回分配内存的字符串的内部辅助函数,但不释放内存,这是一个很明显的自陷。考虑下面的代码:

  1. static char *GetStringValue() {
  2.     ..
  3.     return resultString.ToNewCString();
  4. }
  5.     ..
  6.     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赋值给WamUserToNewCString()会扔掉指针。但返回值从未被释放。因此要么使用自动形式的字符串类,确保当你的字符串超出作用域时会被释放要么确保你的字符串已被释放。

 

 

Automatic cleanup:

自动清除:

  1. static void GetStringValue(nsAWritableCString& aResult) {
  2.     ..
  3.     aResult.Assign("resulting string");
  4. }
  5.     ..
  6.     nsCAutoString warning;
  7.     GetStringValue(warning);
  8.     WarnUser(warning.get());

 

Free the string manually:

手动释放字符串:

 

  1. static char *GetStringValue() {
  2.     ..
  3.     return resultString.ToNewCString();
  4. }
  5.     ..
  6.     char *warning = GetStringValue();
  7.     WarnUser(warning);
  8.     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() 取代nsStringAssignWithConversion 和 AppendWithConversion。在大多数平台上,会强制编译器把它编译成一个unicode字符串,然后直接赋值给它。

 

Incorrect:

不正确的:

  1. nsAutoString warning; warning.AssignWithConversion("danger will robinson!");
  2. ..
  3. foo->SetUnicodeValue(warning.get());

Correct:

正确的:

  1. NS_NAMED_LITERAL_STRING(warning,"danger will robinson!");
  2. ..
  3. // if you'll be using the 'warning' string, you can still use it as before:
  4. foo->SetUnicodeValue(warning.get());
  5. // alternatively, use the wide string directly:
  6. 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.

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

  • 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:

  • 控制结构:

  1. if (...) {
  2. else if (...) {
  3. else {
  4. }
  5. while (...) {
  6. }
  7. do {
  8. while (...);
  9. for (...; ...; ...) {
  10. }
  11. switch (...)
  12. {
  13.   case 1:
  14.     {
  15.       // When you need to declare a variable in a switch, put the block in braces
  16.       int var;
  17.     } break;
  18.   case 2:
  19.     ...
  20.     break;
  21.   default:
  22.     break;
  23. }
  • Classes:

  • 类:

  1. class nsMyClass : public X,
  2.                        public Y
  3. {
  4. public:
  5.   nsMyClass() : mVar(0) { ... };
  6.   
  7. private:
  8.   int mVar;
  9. };
  • Methods:

  • 方法:

  1. int
  2. nsMyClass::Method(...)
  3. {
  4.   ...
  5. }
  • 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-modenil。在新文件中使用2个空格作为进。

  1. /* -*- 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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值