Apollo Best Coding Practice

  1. Always build, test, and lint all.

    ./apollo.sh check
    
  2. Always write unit tests and put them along with the source files.

    foobar.h
    foobar.cc
    foobar_test.cc
    
  3. A Bazel target should contain at most one header and one source file.

    cc_library(
      name = "foobar",
      hdrs = ["foobar.h"],
      srcs = ["foobar.cc"],
      deps = [
        ...
      ],
    )
    
    cc_test(
      name = "foobar_test",
      srcs = ["foobar_test.cc"],
      deps = [
        ":foobar",
        ...
      ]
    )
    

    You can use ./apollo.sh format <path/to/BUILD> to fix BUILD file style issues.

  4. In general, Apollo follows Google C++ coding style. You should run scripts/clang_format.sh <path/to/cpp/dirs/or/files> or ./apollo.sh format -c <path/to/cpp/dirs/or/files> to fix C++ style issues.

  5. Simple and unified function signature.

    // 1. For input objects, const reference guarantes that it is valid, while
    //    pointers might be NULL or wild. Don't give others the chance to break
    //    you.
    // 2. For input scalars, just pass by value, which gives better locality and
    //    thus performance.
    // 3. For output, it's the caller's responsibility to make sure the pointer
    //    is valid. No need to do sanity check or mark it as "OutputType* const",
    //    as pointer redirection is never allowed.
    void FooBar(const InputObjectType& input1, const InputScalaType input2, ...,
                OutputType* output1, ...);
    
    // RVO machanism will help you avoid unnecessary object copy.
    // See https://en.wikipedia.org/wiki/Copy_elision#Return_value_optimization
    OutputType FooBar(const InputType& input);
    
  6. Use const whenever possible.

    // Variables that don't change.
    const size_t current_size = vec.size();
    // Functions that have no side effect.
    const std::string& name() const;
    
  7. Prefer C++ headers over C headers.

    We prefer using #include <ctime> over #include <time.h>, <cmath> over <math.h>, <cstdio> over <stdio.h>, <cstring> over <string.h>, etc.

  8. Include necessary headers only. No more, no less.

    Please also pay attention to header orders. Again, you can use apollo.sh format -c or scripts/clang_format.sh to fix header order issues.

  9. List only direct dependencies in deps section of a Bazel target.

    Generally, only targets to which the included headers belongs should be listed as a dependency.

    For example, suppose sandwich.h includes bread.h which in turn includes flour.h. Since sandwich.h doesn’t include flour.h directly (who wants flour in their sandwich?), the BUILD file would look like this:

    cc_library(
     name = "sandwich",
     srcs = ["sandwich.cc"],
     hdrs = ["sandwich.h"],
     deps = [
         ":bread",
         # BAD practice to uncomment the line below
         # ":flour",
     ],
    )
    
     cc_library(
         name = "bread",
         srcs = ["bread.cc"],
         hdrs = ["bread.h"],
         deps = [":flour"],
     )
    
     cc_library(
         name = "flour",
         srcs = ["flour.cc"],
         hdrs = ["flour.h"],
     )
    
  10. Conform to the DRY principle.

    Don’t repeat yourself, in any way. Avoid duplicate classes, functions, const variables, or a simple piece of code. Some examples:

    • It’s fine to refer a name with full path once, like apollo::common::util::Type, but better to make a short alias if you need to use twice or more: using apollo::common::util::Type;.

    • It’s fine to access a sub-field of proto once in cascade style, like a_proto.field_1().field_2().field_3(), but better to save the reference of a common part first if you need to access it twice or more: const auto& field_2 = a_proto.field_1().field_2();.