The Mesos codebase follows the Google C++ Style Guide with some notable differences, as described below. Note that the clang-format tool can be helpful to ensure that some of the mechanical style rules are obeyed.
using namespace foo
statements as it is not explicit about which symbols are pulled in, and it can often pull in a lot of symbols, which sometimes lead to conflicts.namespace http = process::http;
. These should only be present at the top of the .cpp file.Try(State _state, T* _t = NULL, const std::string& _message = "") : state(_state), t(_t), message(_message) {}
Prefer trailing underscores for use as member fields (but not required). Some trailing underscores are used to distinguish between similar variables in the same scope (think prime symbols), but this should be avoided as much as possible, including removing existing instances in the code base.
If you find yourself creating a copy of an argument passed by const reference, consider passing it by value instead (if you don't want to use a leading underscore and copy in the body of the function):
// You can pass-by-value in ProtobufProcess::install() handlers. void Slave::statusUpdate(StatusUpdate update, const UPID& pid) { ... update.mutable_status()->set_source( pid == UPID() ? TaskStatus::SOURCE_SLAVE : TaskStatus::SOURCE_EXECUTOR); ... }
k
followed by mixed case, e.g. kDaysInAWeek
).// Use `SchedulerDriver::acceptOffers()` to send several offer // operations. This makes use of the `RESERVE()` and `UNRESERVE()` // helpers, which take a `Resources` object as input and produce // appropriate offer operations. Note that we are unreserving the // resources contained in `dynamicallyReserved1`. driver.acceptOffers({offer.id()}, {UNRESERVE(dynamicallyReserved1), RESERVE(dynamicallyReserved2), RESERVE(dynamicallyReserved3)}, filters);
template
keyword, e.g. template <typename T>
rather than template<typename T>
.// 1: OK. allocator->resourcesRecovered(frameworkId, slaveId, resources, filters); // 2: Don't use. allocator->resourcesRecovered(frameworkId, slaveId, resources, filters); // 3: Don't use in this case due to "jaggedness". allocator->resourcesRecovered(frameworkId, slaveId, resources, filters); // 3: In this case, 3 is OK. foobar(someArgument, someOtherArgument, theLastArgument); // 4: OK. allocator->resourcesRecovered( frameworkId, slaveId, resources, filters); // 5: OK. allocator->resourcesRecovered( frameworkId, slaveId, resources, filters);
Try<Duration> failoverTimeout = Duration::create(FrameworkInfo().failover_timeout());
Try<very_very_long_type> long_name = ::protobuf::parse<very_very_long_type>( request); for (int i = 0; i < very_very_long_expression(); i++) { // No empty line here for control constructs. }
We disallow capturing temporaries by reference. See MESOS-2629 for the rationale.
Future<Nothing> f() { return Nothing(); } Future<bool> g() { return false; } struct T { T(const char* data) : data(data) {} const T& member() const { return *this; } const char* data; }; // 1: Don't use. const Future<Nothing>& future = f(); // 1: Instead use. const Future<Nothing> future = f(); // 2: Don't use. const Future<Nothing>& future = Future<Nothing>(Nothing()); // 2: Instead use. const Future<Nothing> future = Future<Nothing>(Nothing()); // 3: Don't use. const Future<bool>& future = f().then(lambda::bind(g)); // 3: Instead use. const Future<bool> future = f().then(lambda::bind(g)); // 4: Don't use (since the T that got constructed is a temporary!). const T& t = T("Hello").member(); // 4: Preferred alias pattern (see below). const T t("Hello"); const T& t_ = t.member(); // 4: Can also use. const T t = T("Hello").member();
We allow capturing non-temporaries by constant reference when the intent is to alias.
The goal is to make code more concise and improve readability. Use this if an expression referencing a field by const
is:
hashmap<string, hashset<int>> index; // 1: Ok. const hashset<int>& values = index[2]; // 2: Ok. for (auto iterator = index.begin(); iterator != index.end(); ++iterator) { const hashset<int>& values = iterator->second; } // 3: Ok. foreachpair (const string& key, const hashset<int>& values, index) {} foreachvalue (const hashset<int>& values, index) {} foreachkey (const string& key, index) {} // 4: Avoid aliases in most circumstances as they can be dangerous. // This is an example of a dangling alias! vector<string> strings{"hello"}; string& s = strings[0]; strings.erase(strings.begin()); s += "world"; // THIS IS A DANGLING REFERENCE!
Mesos source files must contain the “ASF” header:
// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.
Stout and libprocess source files must contain the “Apache License Version 2.0” header:
// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License
In addition to the ordering rules from the Google style guide, Mesos related headers are separated into sections. Newline to separate each section. Mesos related headers in include
directories are partitioned by their subfolders, sorted alphabetically, and included using brackets. Header in src
directories are included afterwards, using the same rules but with quotes instead of brackets.
Example for src/common/foo.cpp
:
#include "common/foo.hpp" #include <stdint.h> #include <string> #include <vector> #include <boost/circular_buffer.hpp> #include <mesos/mesos.hpp> #include <mesos/type_utils.hpp> #include <mesos/module/authenticator.hpp> #include <mesos/scheduler/scheduler.hpp> #include <process/http.hpp> #include <process/protobuf.hpp> #include <stout/foreach.hpp> #include <stout/hashmap.hpp> #include "common/build.hpp" #include "common/protobuf_utils.hpp" #include "master/flags.hpp"
We support C++11 and require GCC 4.8+ or Clang 3.5+ compilers. The whitelist of supported C++11 features is:
auto
and decltype
). The main goal is to increase code readability. This is safely the case if the exact same type omitted on the left is already fully stated on the right. Here are several examples:// 1: OK. const auto i = values.find(keys.front()); // Compare with const typename map::iterator i = values.find(keys.front()); // 2: OK. auto names = shared_ptr<list<string>>(new list<string>()); // Compare with shared_ptr<list<string>> names = shared_ptr<list<string>>(new list<string>()); // 3: Don't use. auto authorizer = LocalAuthorizer::create(acls); // Compare with Try<Owned<LocalAuthorizer>> authorizer = LocalAuthorizer::create();
std::mutex
std::lock_guard<std::mutex>
std::unique_lock<std::mutex>
std::atomic
)std::atomic_int
), in addition to std::atomic<T>
. When a typedef is available, it should be preferred over explicit template specialization of std::atomic<T>
.load
and store
member functions should be used instead of the overloads of operator T()
and operator=
. Being explicit helps to draw the reader's attention to the fact that atomic values are being manipulated.class T : public std::enable_shared_from_this<T>
shared_from_this()
// 1: OK. []() { ...; }; // 2: Don't use. [] () { ...; };
// 1: OK. [=]() { ... }; // Default capture by value. [n]() { ... }; // Explicit capture by value. [&n]() { ... }; // Explicit capture by reference. [=, &n]() { ... }; // Default capture by value, explicit capture by reference. // 2: Don't use. [&]() { ... }; // Default capture by reference.
mutable
only when absolutely necessary.// 1: OK. []() mutable { ...; };
// 1: OK. []() { return true; }; []() -> bool { return ambiguous(); };
auto
when naming a lambda expression:// 1: OK. auto lambda = []() { ...; };
// 1: OK. auto lambda = []() { ...; }; // 2: OK. auto lambda = []() { ...; };
instance.method([]() { ...; });
// 1: OK. instance .method([]() { ...; }) .then([]() { ...; }) .then([]() { ...; }); // 2: OK (when no chaining, compare to 1). instance.method([]() { ...; }); // 3: OK (if no 'instance.method'). function([]() { ...; }) .then([]() { ...; }) .then([]() { ...; }); // 3: OK (but prefer 1). instance.method([]() { ...; }) .then([]() { ...; }) .then([]() { ...; });
// 1: OK. function([&capture1, &capture2, &capture3]( const T1& p1, const T2& p2, const T3& p3) { ...; }); function( [&capture1, &capture2, &capture3]( const T1& p1, const T2& p2, const T3& p3) { ...; }); auto lambda = [&capture1, &capture2, &capture3]( const T1& p1, const T2& p2, const T3& p3) { ...; }; auto lambda = [&capture1, &capture2, &capture3]( const T1& p1, const T2& p2, const T3& p3) { ...; }; // 2: OK (when capture list is longer than 80 characters). function([ &capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2) { ...; }); auto lambda = [ &capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2) { ...; }; // 3: OK (but prefer 2). function([ &capture1, &capture2, &capture3, &capture4](const T1& p1, const T2& t2) { ...; }); auto lambda = [ &capture1, &capture2, &capture3, &capture4](const T1& p1, const T2& p2) { ...; }; // 3: Don't use. function([&capture1, &capture2, &capture3, &capture4](const T1& p1, const T2& p2) { ...; }); auto lambda = [&capture1, &capture2, &capture3, &capture4](const T1& p1, const T2& p2) { ...; }; // 4: Don't use. function([&capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2, const T3& p3) { ...; }); auto lambda = [&capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2, const T3& p3) { ...; }; // 5: Don't use. function([&capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2, const T3& p3) { ...; }); auto lambda = [&capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2, const T3& p3) { ...; }; // 6: OK (parameter list longer than 80 characters). function([&capture1, &capture2, &capture3]( const T1& p1, const T2& p2, const T3& p3, const T4& p4) { ...; }); auto lambda = [&capture1, &capture2, &capture3]( const T1& p1, const T2& p2, const T3& p3, const T4& p4) { ...; }; // 7: OK (capture and parameter lists longer than 80 characters). function([ &capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2, const T3& p3, const T4& p4) { ...; }); auto lambda = [ &capture1, &capture2, &capture3, &capture4]( const T1& p1, const T2& p2, const T3& p3, const T4& p4) { ...; };
Unrestricted Union.
Like the pre-existing union
, we can overlap storage allocation for objects that never exist simultaneously. However, with C++11 we are no longer restricted to having only non-POD types in unions. Adding non-POD types to unions complicates things, however, because we need to make sure to properly call constructors and destructors. Therefore, only use unrestricted unions (i.e., unions with non-POD types) when the union has only a single field. What does this buy us? Now we can avoid dynamic memory allocations for “container” like types, e.g., Option
, Try
, Result
, etc. In effect, we treat the union like a dynamic allocation, calling placement new, new (&t) T(...)
anyplace we would have just called new T(...)
and the destructor t.~T()
anyplace we would have called delete t
.
Constant expressions.
Constant expressions allow the declaration of static non-POD objects while eliminating the unpredictable runtime initialization and destruction issues normally encountered, helping eliminate macros and hard-coded literals without sacrificing performance and type safety. Changes which require converting from constexpr
to const
can propagate through the dependency tree requiring that dependent constexpr
uses also be converted to const
, hence we avoid using constexpr
in complex functions.
constexpr
behaves as a combination of inline
and const
and hence must be defined before use in another constexpr
.
Prefer constexpr
to const
for all constant POD declarations, constexpr
char
arrays are preferred to const
string
literals.
// OK constexpr char LITERAL[] = "value"; // Not OK - not available at compile time for optimization and // definition required in a separate compilation module. const char LITERAL[]; // Not OK - uncertain initialization order, cannot be used in other // constexpr statements. const string LITERAL("value");
constexpr
functions are evaluated at compile time if all their arguments are constant expressions. Otherwise they default to initialization at runtime. However constexpr
functions are limited in that they cannot perform dynamic casts, memory allocation or calls to non-constexpr functions. Prefer constexpr
over const inline functions.
constexpr size_t MIN = 200; constexpr size_t MAX = 1000; constexpr size_t SPAN() { return MAX-MIN; } int array[SPAN()];
Const expression constructors allow object initialization at compile time provided that all the constructor arguments are constexpr
and the constructor body is empty, i.e. all initialization is performed in the initialization list. Classes which provide constexpr
constructors should normally also provide constexpr
copy constructors to allow the class to be used in the return value from a constexpr
function.
class C { public: constexpr C(int _i) : i(_i) {}; constexpr C(const C& c) : i(c.i) {} private: const int i; };
C++11 does not provide constexpr string
or constexpr
containers in the STL and hence constexpr
cannot be used for any class using stout's Error() class.