导航

[翻译]chromium代码风格

Posted on 2010-09-30 09:24  maconel  阅读(4967)  评论(0)    收藏  举报

Chromium的代码风格说明写得很不错,很多细节都很符合我的习惯,比如代码格式和对assert的处理。:-)

hummer有自己的代码风格,不可能完全照搬,但这种风格的思想还是很值得学习的,尤其是assert那部分。
英文水平比较烂,有几句根本就没读懂,大家凑合着看吧。有些逻辑混乱的句子,建议看原文。
 
译文:
 
chromium代码风格

Chromium项目的代码风格遵守Google C++ Style Guide。下面几点是在Google风格的基础上进行的扩展。本文没有提到的规则,都遵守Google风格。如果某规则在Google风格中是可随意的,本文也没有特别说明,那么在Chromium中就也是可随意的。Objective-C/C++代码应该遵守Google Objective-C Style Guide。如果使用emacs,可以使用style definition for c-modePython代码应该遵守PEP-8,不过在Chromium中,把4空格缩进改成2空格缩进,方法和函数名使用混合大小写(MixedCase)风格,而不是带下划线的小写(lower_case_with_underscores)。
当修改代码时,应尽量遵守该代码文件中声明的约定。
改WebKit代码时,应遵守WebKit coding style
下文中所写的规则,专用于Chromium项目。
 
命名
-单元测试和性能测试的命名应分别以_unittest和_perftest结尾,它们应与被测的程序文件放在同一个目录中。
-Win32窗体类的命名应以Chrome_开头(注意大小写)。
-表示数量的变量,应在名字中带上单位。比如kUpdateTimerMsec或num_downloaded_bytes。
-表示web地址时,应该用URL,而不是URI。
-应该使用unix_hacker风格,而不是驼峰法(CamelCase)。
-Google风格强烈建议(不是必须)使用unix_hacker风格来命名内容很简短、影响很小的成员函数,比如那些简单的函数,或者设置读取器函数(getters and setters)。当然,这也要看团队的习惯。有些代码,什么东西都用驼峰法(CamelCase)命名。没必要只为了改几个函数名风格就专门打补丁,但在新代码中,应使用这些命名风格,除非是在封装时驼峰法(CamelCase)更合适(unless there are encapsulation reasons why CamelCase would be much more appropriate)。
-"Chromium"是项目名,而不是产品名,不要把它写到代码或API中。需要的话,可以用"chrome"。
-虽然Google C++ Style Guide中说现在应该用kConstantNaming的风格来命名枚举类型,但现在的代码中,还是在用MACRO_STYLE,以便保持一致性。
 
代码格式
-用Foo* bar和const Foo& bar的形式,而不是Foo *bar和const Foo &bar。
-有条件返回语句时,return应单独一行(方便跟踪)。
-当表达式换行时,操作符应该在上一行的末尾,而不是下一行的开头。比如:
正确:
foo = bar + 
      baz;
错误:
foo = bar 
      + baz;
打印日志是个例外。如果写一行日志超过80个字符,需要换行时,子行应该以<<操作符开头,并且开头的<<应与上一行的<<对齐:
LOG(INFO) << "I have a very long log message here, with multiple things to"
          << " say and some variables to print out, like var1: "
          << var1 << " and var2: " << var2;
-空指针使用NULL,而不是0或者自定义一个null(例外:WebKit风格的代码应该使用0来保持一致性)。
-bool型变量使用true和false。在有些地方,必须使用BOOL型,那么就应该用TRUE和FALSE。
-某些有强迫症的童鞋喜欢把cc文件里的函数顺序和h文件里的函数声明顺序保持一致。Google C++ Style Guide里没有这个要求,如果你碰到有顺序不一致的,应保留它原样不变。
-当被h文件包含时,我们尝试过,并能确定消息映射宏按字母顺序排列的话,更容易扫描。所以请按照此约定添加新的消息处理代码。
-当你实现一个接口时,应把从它继承的函数在h文件里排在一起,与其它声明区分开来,这样就能很清晰地知道它们是实现的同一接口。它们的顺序也必须和基类里的一致,中间不要加注释和空格,这样阅读的时候就能看出基类里的更多信息。
-类的函数定义应明确加上inline关键字来表明它们的编译方式,前提是如果它们确实需要是内联函数。把一个类放在一个cc文件中,把声明和定义分开是个好主意,这样可以增强声明的可读性。
 
平台相关代码
平台相关的代码应该使用build/build_config.h中定义的宏,而不是WIN32这种玩意。现在已经有的平台定义有OS_WIN,OS_MACOSX和OS_LINUX。不要用#else来处理平台相关的代码,应该明确地表明代码是用在哪些平台下的。同时用在Mac和Linux下的代码,可以用OS_POSIX来定义(比如pthreads)。
应该用#if defined(...),而不是#ifdef来处理平台相关代码。这样可以让别人添加#elif defined(...)这样的分支。
错误:
#ifdef OS_WIN
  <Windows code>
#else
  <Mac & Linux code>
#endif
正确:
#if defined(OS_WIN)
  <Windows code>
#elif defined(OS_POSIX)
  <Mac & Linux code>
#endif
如果需要用到wchat_t类型,不要用平台给你定义的,应该用WCHAR_T_IS_UTF16或WCHAR_T_IS_UTF32。
 
工程设置
-不要check-in以vcproj,sln,xcodeproj,scons为后缀的文件,只check-in GYP文件就可以了。我们通常的编译设置在common.gypi中查看。
  -只有一种情况可以接受非gyp工程文件,那就是不用打开工程文件的第三方库。这可以让复制品尽可能与原版保持一致。
-尽量用#pragma而不是工程设置来引入lib文件。
-在编译选项中把waning级别设置为fatal,除非你要编译第三方代码,而它又不容易修改。(即便如此,加上#pragma warning(disable: [warning #])试试看也没啥损失)。
 
静态变量
-不要使用有构造或析构函数,甚至啥也没有的静态变量(尤其是全局变量)。要像避免诡异的crash(就是那种连crash捕捉器都捉不到的crash)那样避免它们。需要的话,就用单例(Singleten)模式吧。
注意,因为这个原因,Google风格禁止定义全局作用域的绝大多数类对象。包括字符串对象,比如std::string!应使用const char* const或const wchat_t* const来代替,注意第二个const。
 
类型
-上面都是基于Google C++ Style Guide来讨论的。包括“用int来处理一般的记数”和“不要用unsigned int因为你不确定结果什么时候会是负数”。但是,还是有几点需要澄清一下:
-处理对象数量或内存数据大小时,比如很大的一个buffer或内存块的字节数,字符串的长度,STL容器中对象的数量,应优先用size_t而不是int。有种情况就是,当调用一个库函数时,它要求传入或返回一个size_t类型变量,那你就应该使用size_t,而不是在它与int之间转来转去。
-有些情况要处理一些数据类型的精确大小(比如32位像素值,位图遮罩(bitmask),精确宽度的计数器),应使用uint32这种类型,而不是size_t。对于有些不在内存中的对象,或者数量超过32位的,比如下载文件的偏移量(可能有好几G),那就应使用uint64。
-跨主机或进程传递数据时,安全起见应使用精确大小的类型,因为发送和接收方可能对int和size_t编译出的大小不同。例如,在IPC层,我们使用uint32来存储“内存中的小对象”。一般不论什么情况,调用这些函数的代码都应该使用原生类型(上面所有规则都适用),有必要的话,应在IPC层的入口处做转换。
-因为有符号类型是病毒,而我们也不可能总是小心翼翼地做到上面几点,所以你可能能找到一些调API时该传size_t却传了int的错误。如果有可能,考虑清理下吧。
 
方法签名
-基于经验和测试,函数通过返回值返回std::[w]string和通过传出参数返回速度是一样的。所以为了可读性,我们鼓励尽量通过返回值返回字符串。
 
文件头
-Chromium项目中所有源文件必须以下面的文字开头。注意,你不用修改已存在的文件里的版权时间,除非你因为什么原因修改过它。
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-Chromium OS项目的源代码以下面文字开头,与Chromium稍有不同(Chromium -> Chromium OS)。
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-Chromium项目在服务器上为一些其它开源项目做了镜像(比如U-Boot)。当为这些项目贡献代码时,请保留原项目的版权头。
-我们没有把作者(Author):这些内容加到源文件中。如果贡献了代码,那么trunk/src/AUTHORS里是没有你或你的组织的名字的,请把你的名字和联系方式加进去。
 
WebKit glue风格
在WebKit转换和glue目录中,你应该能找到WebKit文件。我们已经在WebKit glue源文件里使用了Google风格,除非它包含了WebKit的文件。glue代码应该按WebKit方式包含WebKit h文件(不带额外的路径信息),并带有警告监控包装了WebKit头,这样当这些文件被忽略时,就可以收到警告。包含WebKit时,一定要先包含config.h,否则就会有奇怪的错误!例如:
#include "webkit/glue/me.h"  // Begin with the corresponding header for this .cc file.
#include <stdio.h>  // System includes are second.
#include <stdlib.h>
#pragma warning(push, 0)  // This will make Visual Studio ignore warnings for these files.
#include "config.h"  // WebKit section, note alphabetical order.
#include "Document.h"
#include "Frame.h"
#include "Page.h"
#pragma warning(pop)
#include "base/file_util.h"  // Google-style includes for everything else.
#include "webkit/glue/webkit_glue.h"
 
Svn属性
对于Chromium开发,你应该在~/.subversion/config (On Windows: %APPDATA%\Subversion\config)中,配置http://src.chromium.org/viewvc/chrome/trunk/tools/buildbot/slave/config为自动设置属性。
我们不用svn:eol-style=native。因为这样跨平台移动不同的文件太痛苦,尤其是try server。
 
CHECK()、DCHECK()和NOTREACHED()大PK
CHECK()用来检查断言,如果不满足条件就会立刻crash。DCHECK()和CHECK()差不多,但它只在debug编译时起作用。NOTREACHED()只在debug下中断,相当于DCHECK(false)。这些宏定义在base/logging.h里。
使用DCHECK或NOTREACHED在开发者滥用某函数时通知他们,或者出问题时,让他们能意识到问题的存在。通常情况下——这看起来违背了我们的直觉——让程序直接crash而不是DCHECK失败会更好一些。一条语句会导致DCHECK失败,而企图用一个优雅的方法去处理这个DCHECK失败,这本身就违背了写DCKECK的初衷。(如果你发现自己就在写这样的代码,那可能你实际是想用DLOG或LOG。)在有些情况下,DCHECK失败的后果,也可能会导致crash。这不是坏事。crash将被记入日志,而且其实在更广范围内导致的DCHECK失败也是可以预见的。收到一个crash的错误报告,远远好于一个含糊的出错描述,不可重现的挂起、僵死,或其它不知道怎么搞出来的非crash错误。
有时通过CHECK强制产生一个crash还更好。这包括处理不信任的输入(比如非法路径),继续操作可能导致安全漏洞等情形。用CHECK来帮助杜绝观察的源(the source of an observed),而不是迟钝的crash报告。开发者可以在crash附近再放几个CHECK,以便在crash时得到更多信息。
在处理异常的不可信任的输入时,除了CHECK,有时还有更好的选择。例如,如果一个页面渲染器(renderer)发送一个异常IPC给浏览器进程,那杀掉出错的渲染器总比让浏览器进程crash掉强。
 
 
 
原文:
  
 

Coding Style

Coding style for the Chromium projects generally follows the Google C++ Style Guide. The notes below are usually just extensions beyond what the Google style guide already says. If this document doesn't mention a rule, follow the Google C++ style. If Google style is flexible on a particular topic, and this document doesn't clarify below, then Chromium style is also flexible on that topic. Objective-C/C++ code should follow the Google Objective-C Style Guide. If you use emacs, you may find Google's style definition for c-mode helpful. Python code should follow PEP-8, except that Chromium uses two-space indentation instead of four-space indentation, and it uses MixedCase for method names and function names instead of lower_case_with_underscores.

Try to follow any per-file conventions you notice when modifying existing code.

When working on WebKit code, you should use the WebKit coding style instead.

The remainder of this page covers rules that are specific to the Chromium projects.

 

Naming

  • Unit tests and performance tests should be named with the suffixes _unittest and _perftest, respectively, and placed alongside (in the same directory as) the file containing the functionality they're testing.
  • Win32 window class name strings should begin with Chrome_ (note the capitalization).
  • Variables representing quantities should express the units of measurement in the name unless they're extremely obvious in context, e.g. kUpdateTimerMsec ornum_downloaded_bytes
  • Use URL instead of URI when referring to web addresses.
  • Projects should be named using unix_hacker style, not CamelCase style.
  • Google style strongly encourages (but does not mandate) unix_hacker style on member functions that do so little work they're effectively free, such as simple, non-virtual getters and setters. This is also the team position. Some code uses CamelCase for everything; avoid writing patches just to convert these functions to unix_hacker style, but try to use it in new code you write unless there are encapsulation reasons why CamelCase would be much more appropriate.
  • "Chromium" is the name of the project, not the product, and should never appear in code in variable names, API names etc. Use "chrome" instead.
  • Though the style guide says to use kConstantNaming for enums now, we still use MACRO_STYLE naming for enums for consistency with existing code.

 

Code formatting

  • Foo* bar and const Foo& bar, not Foo *bar and const Foo &bar.
  • For conditional return statements, the return should be indented on its own line (for easier tracing).
  • When expressions are wrapped, the operator should be on the end of the broken line, not the start of the new line. For example:

 

foo = bar + 
      baz;

not:

 

foo = bar 
      + baz;

An exception is log messages. When you have a log line that is longer than 80 characters, subsequent lines should start with the << operator and should be aligned based on the first << from the original line:

 

LOG(INFO) << "I have a very long log message here, with multiple things to"
          << " say and some variables to print out, like var1: "
          << var1 << " and var2: " << var2;

 

  • Use NULL for null pointers rather than 0 or defining null (exception: code that needs to follow WebKit style conventions should use 0).
  • Use true and false for bool values. In some places where we've been forced to use BOOL, we use TRUE and FALSE.
  • Some of us have OCD and like to make sure the order of the function implementations in our .cc files matches the order in the .h files. While this isn't a rule in the Google C++ style guide, if you find a file in this condition please try to leave it in this state after modifying it.
  • As with header includes, we try and make sure message map macros are in alphabetical order for ease of scanning. Please follow this convention when adding new message handlers.
  • When you implement an interface, group those functions in your header file in one section, clearly labeled as implementing that interface. Be sure to preserve the order of the virtual base class, and do not add comments or whitespace between them, as the reader can refer to the virtual base class for more information.
  • Defining a function in the class declaration is an implicit instruction to the compiler to inline the function.  Only do this when you truly want the function to be inlined.  Even with a class that's local to a single .cc file, declaring and defining things separately is a good idea, and keeps the declaration more readable.

 

Platform-specific code

Platform-specific code should use the system specific defines in build/build_config.h and not other things such as WIN32. Current platforms are OS_WINOS_MACOSX, andOS_LINUX. Don't use an #else clause to define platform behavior, it should opt-in to everything explicitly. Use OS_POSIX to define something applicable for Mac and Linux (for example, pthreads).
Prefer #if defined(...) rather than #ifdef for platform-specific code. This lets somebody else add an #elif defined(...) later on which will match.
Bad:
#ifdef OS_WIN
  <Windows code>
#else
  <Mac & Linux code>
#endif
Good:
#if defined(OS_WIN)
  <Windows code>
#elif defined(OS_POSIX)
  <Mac & Linux code>
#endif
If you need to do something based on the type of wchar_t, don't use platform defines. Use WCHAR_T_IS_UTF16 or WCHAR_T_IS_UTF32 instead.

Project settings

  • Don't check-in .vcproj, .sln, .xcodeproj or .scons files. Use GYP instead. Most our common build settings are in common.gypi.
    • The only reason we may accept non-gyp project files is that they are from third parties and aren't used. This is to keep forks as close as possible to upstream.
  • Try to use #pragma lib where needed instead of explicitly adding library dependencies in project files.
  • Compile with warnings set to fatal, unless you are compiling third-party code which you cannot easily fix. (And even then, it might be worth seeing if you can use #pragma warning(disable: [warning #]).

Static variables

  • Don't use static variables (especially globals) that have constructors or destructors, even trivial. In general, just avoid them altogether as they can lead to mysterious crashes (that our crash reporter will not be able to catch). Use Singleton instead.

     

    Note that Google style bans global-scope class objects of most types for this reason. This includes string objects like std::string! Use const char* const or const wchar_t* const when you need static strings. Note the second const.

Types

  • We generally follow the Google C++ Style Guide as mentioned above.  This includes recommendations like "use int for general-purpose counting" and "don't use unsigned intjust because you don't expect the result to be negative".  However, there are a few points worth clarifying:
  • When you are dealing with a count of objects or bytes in memory, e.g. the length of a buffer/block of bytes, the size of a string, or the number of objects in an STL container, use size_t rather than int.  A good clue is that when you're calling library functions that take or return size_t, you should be using size_t as well rather than casting or coercing to int.
  • In cases where the exact size of the type matters (e.g. a 32-bit pixel value, a bitmask, or a counter that has to be a particular width), use a sized type like uint32, not size_t.  For objects that aren't in memory or may need more than 32 bits, e.g. offsets into a downloaded file (which could be several gigabytes in size), use something like uint64.
  • When passing values across network or process boundaries, use explicitly-sized types for safety, since the sending and receiving ends may not have been compiled with the same sizes for things like int and size_t.  For example, in the IPC layer, we use uint32 for the sizes of "small objects in memory".  Generally, code that calls these functions should use whatever types would be natural (per the rules above), and if necessary cast on entry to the IPC layer.
  • Because signedness is viral, and we haven't always been careful about following the above, you may find yourself calling APIs that take int where they should take size_t.  Consider cleaning up these APIs if it's feasible to do so.

 

Method signatures

  • Based on some empirical testing, it seems just as fast to return a std::[w]string from a function as a return value as it is to pass it out as an out param. So we encourage passing strings out as return values when possible for the sake of readability.

File headers

  • All source files in the Chromium project must start with the following header. Note that you don't need to update the copyright date on an old file unless you're already editing it for some other reason.
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
  • The Chromium OS project's source files start with a slightly different header (Chromium -> Chromium OS), like this:

     

         // Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
         // Use of this source code is governed by a BSD-style license that can be
         // found in the LICENSE file.

  • The Chromium project hosts mirrors of some upstream open-source projects (for example U-Boot).  When contributing to these portions of the code repository, please retain the original project's copyright headers.
  • We don't add Author: lines to our files. Instead, if you contribute changes and your name or your organization's name isn't already in trunk/src/AUTHORS, please add your name and contact information.

WebKit glue style

You should only be including WebKit files in our WebKit port and glue directories. We always use Google style for WebKit glue files with the exception of when WebKit files need to be included. Glue code should include WebKit headers the WebKit way (with no extra path information) and wrap WebKit headers with warning guards so that warnings in those files are ignored. Be sure to include config.h first when you are using WebKit includes, or you will get weird errors! Example:
#include "webkit/glue/me.h"  // Begin with the corresponding header for this .cc file.

 

#include <stdio.h>  // System includes are second.
#include <stdlib.h>

#pragma warning(push, 0)  // This will make Visual Studio ignore warnings for these files.
#include "config.h"  // WebKit section, note alphabetical order.
#include "Document.h"
#include "Frame.h"
#include "Page.h"
#pragma warning(pop)

#include "base/file_util.h"  // Google-style includes for everything else.
#include "webkit/glue/webkit_glue.h"

 

Subversion properties

For Chromium development, you should configure your ~/.subversion/config (On Windows: %APPDATA%\Subversion\config) file to automatically set  properties on new files: http://src.chromium.org/viewvc/chrome/trunk/tools/buildbot/slave/config

We do not use svn:eol-style=native. It is a pain to move diff files across platforms, especially for the try server.

CHECK() vs DCHECK() and NOTREACHED()

The CHECK() macro is used for checking assertions, and will cause an immediate crash if its assertion is not met. DCHECK() is like CHECK() but is only compiled in on debug builds. NOTREACHED() is a debug-only break, equivalent to DCHECK(false). These macros are defined in base/logging.h.
Use DCHECK or NOTREACHED to inform developers when they are misusing a function or when a bad condition arises that the developer should be made aware of. In general--and this may seem counter-intuitive — it is preferable to crash instead of handling a DCHECK failure. Attempting to handle a DCHECK failure in a graceful manner is a statement that the DCHECK can fail, which contradicts the point of writing the DCHECK. (If you find yourself writing such code, then perhaps what you really want is to use DLOG or LOG.) In some cases the consequences of a failed DCHECK may result in a crash. This is not necessarily a terrible thing. The crash will be logged, and that will result in visibility to the fact that the DCHECK fails in the wild. It is far better to receive detailed crash reports than vague anecdotes about unreproducible hangs, dead clicks, or other misbehaving non-crash conditions.
Sometimes it is preferable to force a crash to happen via CHECK. Those cases include situations where you are dealing with untrusted input (provided there is no reasonable recovery path) and the consequences of continuing execution could result in a security vulnerability.  It is also common practice to use CHECK to help root out the source of an observed, but obtuse, crash report. Developers may sprinkle CHECKs around the crash site to reveal more information about the crash.
Sometimes there are better options besides CHECK when dealing with malformed input from an untrusted source. For example, if a renderer sends the browser process a malformed IPC, it is better to just kill the offending renderer than to crash the browser process.