styleguide/cc_style.md
At Pixie, we mostly follow the Google C++ style guide, but we do deviate from that style in some ways and have yet to decide a style in others. This document serves as a place where we can collect our unique Pixie style decisions and any open issues and their resolutions.
Style: Use appropriate string formatting functions in different scenarios.
Example:
absl::Substitute("[PID=$0] Unexpected event. Message: msg=$1.", pid, msg);
Note: The use of the standard << operator to create formatted strings is prohibited.
Comments: absl::Substitute is easy to use, and is slightly faster than absl::StrFormat() — even though we don’t typically use either in any performance critical code. Note that we previously used absl::StrFormat() in many places, but we should only use StrFormat() when it’s more powerful formatting is actually required.
Style: Default arguments are allowed in certain cases, with the general principle that it should not make the code hard to read. Specifically, the use of default arguments are subject to the following rules:
Example:
// This is okay, because it is easy to follow and is on a primitive type.
void Init(int level = 0);
Style: Include the name of inlined literals when using them in a function call, with the name before the literal value.
Example:
Compile(code, /* opt_level */ 0);
Style: If the variable is a count of or index into structures in memory, then always use size_t.
This rule does not apply to any countable value, however, as counts in time (e.g. stats counters) should not use size_t.
Opens: For all other cases
Should we distinguish const vs. non-const accessors? I used to have mutable_ prefix on non-const accessor, and they should all return pointers (not non-const reference). The reason is to distinguish between 2 more clearly.
Always use enum class, not enum. Enum values should be named in the same style of constants. (Google’s style guide allow MACRO_LIKE_NAME for enums).
When use enum value in switch statement, do not provide default branch. This way, the compiler can catch missed values.
Proposed Style: Use static_cast for both up-casting and down-casting. Avoid the use of dynamic_cast.
Rationale: dynamic_cast has a run-time cost, and we should be using casts in places where we know which type we are casting to.
Alternative: static_cast for up casting, dynamic_cast for down casting.
Rationale: Nudge against down casting. More readable to understand the intention.
Style: Use structs to return multiple values. Avoid tuples.
Rationale: Provides clarity on what the return values are.
//OPTION 1
namespace pl {
namespace einstein {
struct Energy {
int a;
int b;
}
struct Mass {
double m;
double exp;
}
}
}
//OPTION 2
namespace pl {
namespace einstein {
struct Energy {
int a;
int b;
}
struct Mass {
double m;
double exp;
}
}
}
CHECK(<true|false>)and CHECK_*(<expression>): Unconditionally crash if false.
CHECK() on flag values would be an ideal use of CHECK.DCHECK(<true|false>) and DCHECK_*(<expression>): In debug build, same as CHECK(); in non-debug build, this will be removed by compiler.
DCHECK() on nested loop conditions would be ideal.ECHECK(<true|false>) and ECHECK_*(<expression>): In debug build, same as CHECK(); in non-debug build, this causes a LOG(ERROR).
CHECK(false) vs LOG(FATAL)And similarly, ECHECK(false) vs LOG(DFATAL).
Example with a switch statement. Note that this is not specific to a switch statement, though. The style question is relevant where ever a CHECK(false) may be used.
switch (direction) {
case kRequest:
// Do something.
break;
case kResponse:
// Do something.
break;
default:
// Option 1:
CHECK(false) << "Unexpected direction";
// Option 2:
LOG(FATAL) << "Unexpected direction"
}
emplace vs try_emplacetry_emplace is the newer version of emplace. Prefer try_emplace instead of emplace unless the object is not moveable. Note this is also the recommendation of absl.
Inheritance is usually difficult to maintain than holding a member variable.
Inheritance puts a "Is A" relationship with the subclasses in its semantic, which can easily diverge, and causes confusions.
Eg: The connection between BCCWrapper and BCCSymbolizer is not obvious.
Inheritance also makes it easier to accidentally use the parent class method without a clear indication.
Eg: There is no way to tell if a function called inside BCCSymbolizer is from BCCWrapper or its own.
It also makes protected member available to subclasses, which also extend the scope of misuse.
Benefits of inheritance is convenience, where accessing parent class' non-private member is without any restriction.