「Dev:Doc/Code Style」の版間の差分

提供: wiki
移動先: 案内検索
 
(1版 をインポートしました)
 
(相違点なし)

2018年6月29日 (金) 05:44時点における最新版

Coding Style (C/C++)

There are only two important rules:

  • When making changes, conform to the style and conventions of the surrounding code.
  • Strive for clarity, even if that means occasionally breaking the guidelines. Use your head and ask for advice if your common sense seems to disagree with the conventions.


Language/Encoding

There are some over-arching conventions for Blenders code base.

  • American Spelling for all doc-strings variable names and comments.
  • Use ASCII where possible, avoid special unicode characters such as '÷', '¶' or 'λ'.
  • Use UTF8 encoding for all source files where unicode characters are required.

Naming

  • Use descriptive names for global variables and functions.
  • Public function names should include module identifier, object and property they're operating and operation itself. Very familiar with RNA callbacks names: BLI_object_foo_get(...) / BLI_object_foo_set(...):
/* Don't: */
ListBase *curve_editnurbs(Curve *cu);
/* Do: */
ListBase *BLI_curve_editnurbs_get(Curve *cu);
  • Local variables should be short and to the point.

Macros, Enums, Inline functions

  • Names of macros defining constants and labels in enums should be capitalized.
  • Macro names should be capitalized.
  • Enums used in DNA files should have explicit values assigned.

Function arguments

Return arguments

In C its common to use arguments to return values (since C only supports returning a single value).

  • return arguments should have a r_ prefix, to denote they are return values.
  • return arguments should be grouped at the end of the argument list.
  • optionally, put these arguments on a new line (especially when the argument list is already long and may be split across multiple lines anyway).
/* Don't: */
void BKE_curve_function(Curve *cu, int *totvert_orig, int totvert_new, float center[3]);

/* Do: */
void BKE_curve_function(
        Curve *cu, int totvert_new,
        int *r_totvert_orig, float r_center[3]);

Note, some areas in blender use a _r as a suffix, eg center_r, while this is NOT our convention, we choose not to change all code at this moment.

Value Literals

  • float/double (f only for floats):
    use: 0.3f, 1.0f
    not: .3, 1.f
  • bool:
    use: true, false
    not: 1, 0

Unsigned/Signed Qualifiers

  • The unsigned qualifier should not be used alone, even if it's equivalent to unsigned int:
/* Don't: */
unsigned foo = bar;
/* Do: */
uint foo = bar;  /* preferred for new code. */
unsigned int foo = bar;

Operators and Statements

  • Spaces should be used around assign operators (=, +=, /= and so), one space on each side of it:
/* Don't: */
foo= bar;
/* Do: */
foo = bar;
  • Space should be used between statement and brace:
/* Don't: */
for(ob = bmain->object.first; ob; ob = ob->id.next) {}
for( ob = bmain->object.first; ob; ob = ob->id.next ) {}
/* Do: */
for (ob = bmain->object.first; ob; ob = ob->id.next) {}
  • The "pointer qualifier, '*', should be with the variable name rather than with the type.
/* Don't: */
	char* s;
/* Do: */
	char *s;

Switch Statement

The body of switch statement should be indented, so case keyword has next indentation level relative to switch, body of case statement has got next indentation level relative to case:

/* Don't: */
switch(wmn->category) {
case NC_SPACE:
    ED_region_tag_redraw(ar);
    break;
default:
    break;
}
/* Do: */
switch (wmn->category) {
    case NC_SPACE:
        ED_region_tag_redraw(ar);
        break;
    default:
        break;
}

There are some conventions to help avoid mistakes.

  • blocks of code in a case must end with a break statement, or the comment: /* fall-through */
    Without this its hard to tell when a missing break is intentional or not.
  • when a block of code in a case statement uses braces, the break statement should be within the braces too.
/* Don't: */
switch(value){
case TEST_A: {
    int a = func();
    result = a + 10;
}
break;
case TEST_B:
    func_b();
case TEST_C:
case TEST_D: {
    func_c();
}
break;
}

/* Do: */
switch (value) {
    case TEST_A:
    {
        int a = func();
        result = a + 10;
        break;
    }
    case TEST_B:
        func_b();
        /* fall-through */
    case TEST_C:
    case TEST_D:
    {
        func_c();
        break;
    }
}

Braces

This section covers placement of braces.

Functions should always have braces on new lines.

/* Don't: */
void func(void) {
    return 1;
}
/* Do: */
void func(void)
{
    return 1;
}

For everything else, braces should be placed on the same line.

/* Don't: */
    if (a == b)
    {
        d = 1;
    } else {
        c = 2;
    }
/* Do: */
    if (a == b) {
        d = 1;
    }
    else {
        c = 2;
    }
/* Don't: */
    for (i = 0; i < 10; i++)
        {
        func();
        }
/* Do: */
    for (i = 0; i < 10; i++) {
        func();
    }

Always Use Braces

Braces are to be used even when not strictly necessary (omission can lead to errors).

/* Don't: */
    if (a == b)
        d = 1;
    else
        c = 2;

/* Do: */
    if (a == b) {
        d = 1;
    }
    else {
        c = 2;
    }
/* Don't: */
    for (int i = 0; i < 3; i++)
        dest[i] = src[i];

/* Do: */
    for (int i = 0; i < 3; i++) {
        dest[i] = src[i];
    }

Exceptions to Braces on Same Line

When flow control (if, for, while) is broken up into multiple lines, it reads better to have the brace on its own lines.

/* Don't: */
    if ((very_long_function_check(that, has, many, arguments)) &&
        (another_very_long_function(some, more, arguments)) &&
        some_more_checks_this_gets_tedious(but, its, readable)) {
        some_other_check_this_could_be_confusing ? func_a() : func_b();
    }

/* Do: */
    if ((very_long_function_check(that, has, many, arguments)) &&
        (another_very_long_function(some, more, arguments)) &&
        some_more_checks_this_gets_tedious(but, its, readable))
    {
        (some_other_check_this_could_be_confusing && n) ? func_a() : func_b();
    }

Notice how the lines run together when the brace doesn't get its own line.

/* Don't: */
	for (a = long_function_name();
	     a < some_long_check;
	     a = some_long_iterator()) {
	    *a_other = a;
	}

/* Do: */
	for (a = long_function_name();
	     a < some_long_check;
	     a = some_long_iterator())
	{
	    *a_other = a;
	}

Braces with Macros

When a macro simply replaces a for loop, brace is on same line, eg...

#define BM_ITER_MESH(ele, iter, bm, itype) \
       for (ele = BM_iter_new(iter, bm, itype, NULL); ele; ele = BM_iter_step(iter))
    BM_ITER_MESH (h, &iter, bm, t) {
        /* body */
    }

But for macros that have a begin/end, do this...

    RNA_PROP_BEGIN (ptr, itemptr, prop)
    {
        /* body */
    }
    RNA_PROP_END;

Indentation

  • In C/C++ sources use TABs for indentation. Spaces are only allowed to keep wrapped part of line aligned with first part of line.
  • The whole idea behind indentation is to clearly define where a block of control starts and ends. Always indent when needed - that is, after if, do, while statements, function declarations, and so on.
  • When defining an argument list over multiple lines, the lines preferable be indented with 8 spaces or align to the opening parenthesis for shorter functions. For example:
/* Don't: */
void function_foo(arg1, arg2, ..., argN,
    argN + 1, ..., argM,
    argM + 1, argM + 2);

void function_foo_with_a_long_name_that_causes_a_lot_of_indent_alignment(arg1,
                                                                         arg2,
                                                                         arg3);
/* Do: */
void function_foo(
        arg1, arg2, ..., argN,
        argN + 1, ..., argM,
        argM + 1, argM + 2);

void short_function(arg1,
                    arg2,
                    arg3);

/* --- snip --- *//* --- 120 chars long --- */
  • When splitting if statement it's preferable to split it on disjunction operation and keeping wrapped conditions:
/* in this case wrapped opening brace is reading easier */

if ((size = VectorObject_Check(value)     ? ((VectorObject *)value)->size : 0) ||
    (size = EulerObject_Check(value)      ? 3 : 0) ||
    (size = QuaternionObject_Check(value) ? 4 : 0) ||
    (size = ColorObject_Check(value)      ? 3 : 0))
{
	/* code - snip */
}

Preprocessor Indentation

Preprocessor directives should never be indented, the hash/pound (# character) should always be the first character on the line, however indentation after the hash can be used.

/* DON'T */
int test(void)
{
    #ifdef WIN32
    int a;
    #else
    size_t a;
    #endif
}


/* DO */
int test(void)
{
#ifdef WIN32
    int a;
#else
    size_t a;
#endif
}


In some cases however you may want to indent the text after the #.

For short blocks this isn't needed, however when blocks of definitions become confusing, 2 space indent should be used after the # symbol. eg.

/* DON'T */
#ifdef __BLI_MATH_INLINE_H__
#ifdef _MSC_VER
#define MINLINE static __forceinline
#define MALWAYS_INLINE MINLINE
#else
#define MINLINE static inline
#define MALWAYS_INLINE static inline __attribute__((always_inline))
#endif
#else
#define MINLINE
#define MALWAYS_INLINE
#endif


/* DO */
#ifdef __BLI_MATH_INLINE_H__
#  ifdef _MSC_VER
#    define MINLINE static __forceinline
#    define MALWAYS_INLINE MINLINE
#  else
#    define MINLINE static inline
#    define MALWAYS_INLINE static inline __attribute__((always_inline))
#  endif
#else
#  define MINLINE
#  define MALWAYS_INLINE
#endif

Trailing Space

In general, avoid trailing white-space.

However this doesn't impact on code readability, so avoid large bulk code-cleanups for existing code (unless the module owner agrees).

Breaking long lines

  • Lines should not be longer than 120 characters (columns) long; limiting to 80 characters is preferred.
  • Statements longer than 120 columns will be broken into sensible chunks. Descendants are always substantially shorter than the parent and are placed substantially to the right. The same applies to function headers with a long argument list. Long strings are as well broken into shorter strings.

Generally the only exceptions are for comments with example commands or URLs - to make cut and paste easier.

The other exception is for those rare cases where letting a line be longer (and wrapping on an 120-character window) is actually a better and clearer alternative than trying to split it into two lines. Sometimes this happens, but it's extremely rare.

DO NOT alter somebody else's code to re-wrap lines (or change whitespace) just because you found something that violates the rules. Let the group/author/leader know, and resist the temptation to change it yourself.

Comments

Style

  • Comments should explain what the code is doing, not how. The how should be more or less obvious from the way the code is written.
  • C code should use C-style comments only:
/* This is a C comment. */
// This is a C++ comment, not a C comment.
  • It is preferred to use C-style comments in C++ code too.
  • When using multiline comments, markers (star character, '*') should be used in the beginning of every line of comment:
/* Special case: ima always local immediately. Clone image should only
 * have one user anyway. */

NOT

/* Special case: ima always local immediately. Clone image should only
   have one user anyway. */
  • XXX marker should be used only in comments, describing usage of a non-obvious solution caused by some design limitations which better be resolved after rethinking of design.
  • Comments should always start with a capital letter and end with a full stop.
/* My small comment. */

NOT

/* my small comment */

API Docs

When writing more comprehensive comments that include for example, function arguments and return values, cross references to other functions... etc, we use DoxyGen syntax comments.

If you choose to write doxygen comments, here's an example of a typical doxy comment (many more in blenders code).

/**
 * Return the unicode length of a string.
 *
 * \param start: the string to measure the length.
 * \param maxlen: the string length (in bytes)
 * \return the unicode length (not in bytes!)
 */
size_t BLI_strnlen_utf8(const char *start, const size_t maxlen)
{
    ...
}

Note that this is just the typical paragraph style used in blender with an extra leading '*'.

As for placement of documentation, we try to keep the comments close to the code. Terse, single line descriptions of functions in headers is fine, but for detailed explanations, add this directly about the code.

In Summery

  • Optionally use doxygen comments for detailed API docs.
  • Keep comments close to code.
  • Comments in header are optional but keep brief.

Utility Macros

Typically we try to avoid wrapping functionality into macros, but there are some limited cases where its useful to have standard macros, which can be shared across the code-base.

Currently these are stored in BLI_utildefines.h.


A brief list of common macros we suggest to use:

  • MIN2(a, b),MAX2,3,4: Return min/max value.
  • SWAP(type, a, b): Swap 2 values.
  • ELEM(value, other, vars...) ...: Check if the first argument matches one of the following values given.
  • ABS(value): Macro version of abs(), Returns the positive value.
  • CLAMP(value, min, max): Clamp a value within a range.
  • CLAMPIS(value, min, max): Return a value clamped by a range.
  • SET_INT_IN_POINTER(value), GET_INT_FROM_POINTER: warning free int/pointer conversions (for use when it wont break 64bit).
  • STRINGIFY(id): Represent an identifier as a string using the preprocessor.
  • STREQ(a, b), STRCASEEQ(a, b): String comparison to avoid confusion with different uses of strcmp().
  • STREQLEN(a, b, len), STRCASEEQLEN(a, b, len): Same as STREQ but pass a length value.


Other utility macros:

  • AT: Convenience for __file__:__line__.
    Example use: printf("Current location " AT " of the file\n");
  • BLI_assert(test): Assertion that prints by default (only aborts when WITH_ASSERT_ABORT is defined).
  • BLI_INLINE: Portable prefix for inline functions.

Many lesser used macros are defined in BLI_utildefines.h, but the main ones are covered above.

UI Messages

Common rules
  • “Channel” identifiers, like X, Y, Z, R, G, B, etc. are always capitalized!
  • Do not use abbreviations like “verts” or “VGroups”, always use plain words like “vertices” or “vertex groups”.
  • Do not use English contractions like “aren’t”, “can’t”, etc. Better to keep full spelling, “are not” or “cannot” are not that much longer, and it helps keeping consistency styling over the whole UI.
  • Some data names are supposed to be “title cased” (namely datablocks), even in tips. However, it is a very fuzzy rule (e.g. vertex groups are not datablocks…), so better never use such emphasis if you are unsure.
UI labels
  • They must use English “title case”, i.e. each word is capitalized (Like In This Example).
UI tooltips
  • They are built as usual sentences. However:
    • They should use infinitive as much as possible: « Make the character run », not « Makes the character run ».
    • They must not end with a point. This also implies they should be made of a single sentence (“middle” points are ugly!), so use comas and parenthesis : « A mesh-like surface encompassing (i.e. shrinkwrap over) all vertices (best results with fewer vertices) », not « A mesh-like surface encompassing (i.e. shrinkwrap over) all vertices. Best results with fewer vertices. »

File Size

If possible try keep files under roughly 4000 lines of code. While there will be exceptions to this rule, you might consider if files over this size can be logically split up.

This is more a rule of thumb, not a hard limit.

Blender Game Engine

The game engine code was mostly written before the code style described on this page was enforced. When you edit game engine code, do not put too much emphasis on keeping a consistent style with existing game engine code. Following this style guide is preferred for new and modified code.

For some background on this, read the mailing list thread "Code style in the BGE".

Code Style (Python)

Python code should adhere to PEP 8, with the following clarifications:

In summary:

  • Only use four spaces for indentation, no tabs.
  • Spaces around operators (except for keyword arguments).
  • CamelCase for classes & exception types underscore_case for everything else.


Scripts that have the comment:

# <pep8 compliant>

... lets other developers know your script is meant to be pep8.

Exceptions to pep8

  • Line width:
    Maximum width for lines is 120 for all scripts.
    Optionally you can use shorter line width, using the comment:
    # <pep8-80 compliant>
    
  • Imports:
    We often put imports within functions body which is not pep8 compliant.
    This is done to speed up Blender's startup times to avoid loading in many files and libraries which the user may never access.

Related Topics