[
https://issues.apache.org/jira/browse/MESOS-2629?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Marco Massenzio updated MESOS-2629:
-----------------------------------
Sprint: Mesosphere Q2 Sprint 8 - 5/1, Mesosphere Q1 Sprint 9 - 5/15,
Mesosphere Sprint 10, Mesosphere Sprint 11 (was: Mesosphere Q2 Sprint 8 - 5/1,
Mesosphere Q1 Sprint 9 - 5/15, Mesosphere Sprint 10)
> Update style guide to disallow capture by reference of temporaries
> ------------------------------------------------------------------
>
> Key: MESOS-2629
> URL: https://issues.apache.org/jira/browse/MESOS-2629
> Project: Mesos
> Issue Type: Task
> Components: documentation, technical debt
> Reporter: Joris Van Remoortere
> Assignee: Joris Van Remoortere
>
> We modify the style guide to disallow constant references to temporaries as a
> whole. This means disallowing both (1) and (2) below.
> h3. Background
> 1. Constant references to simple expression temporaries do extend the
> lifetime of the temporary till end of function scope:
> * Temporary returned by function:
> {code}
> // See full example below.
> T f(const char* s) { return T(s); }
> {
> const T& good = f("Ok");
> // use of good is ok.
> }
> {code}
> * Temporary constructed as simple expression:
> {code}
> // See full example below.
> {
> const T& good = T("Ok");
> // use of good is ok.
> }
> {code}
> 2. Constant references to expressions that result in a reference to a
> temporary do not extend the lifetime of the temporary:
> * Temporary returned by function:
> {code}
> // See full example below.
> T f(const char* s) { return T(s); }
> {
> const T& bad = f("Bad!").Member();
> // use of bad is invalid.
> }
> {code}
> * Temporary constructed as simple expression:
> {code}
> // See full example below.
> {
> const T& bad = T("Bad!").Member();
> // use of bad is invalid.
> }
> {code}
> h3. Mesos Case
> - In Mesos we use Future<T> a lot. Many of our functions return Futures by
> value:
> {code}
> class Socket {
> Future<Socket> accept();
> Future<size_t> recv(char* data, size_t size);
> ...
> }
> {code}
> - Sometimes we capture these Futures:
> {code}
> {
> const Future<Socket>& accepted = socket.accept(); // Valid c++, propose
> we disallow.
> }
> {code}
> - Sometimes we chain these Futures:
> {code}
> {
> socket.accept().then(lambda::bind(_accepted)); // Temporary will be valid
> during 'then' expression evaluation.
> }
> {code}
> - Sometimes we do both:
> {code}
> {
> const Future<Socket>& accepted =
> socket.accept().then(lambda::bind(_accepted)); // Dangerous! 'accepted'
> lifetime will not be valid till end of scope. Disallow!
> }
> {code}
> h3. Reasoning
> - Although (1) is ok, and considered a
> [feature|http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/],
> (2) is extremely dangerous and leads to hard to track bugs.
> - If we explicitly allow (1), but disallow (2), then my worry is that someone
> coming along to maintain the code later on may accidentally turn (1) into
> (2), without recognizing the severity of this mistake. For example:
> {code}
> // Original code:
> const T& val = T();
> std::cout << val << std::endl;
> // New code:
> const T& val = T().removeWhiteSpace();
> std::cout << val << std::endl; // val could be corrupted since the destructor
> has been invoked and T's memory freed.
> {code}
> - If we disallow both cases: it will be easier to catch these mistakes early
> on in code reviews (and avoid these painful bugs), at the same cost of
> introducing a new style guide rule.
> h3. Performance Implications
> - BenH suggests c++ developers are commonly taught to capture by constant
> reference to hint to the compiler that the copy can be elided.
> - Modern compilers use a Data Flow Graph to make optimizations such as
> - *In-place-construction*: leveraged by RVO and NRVO to construct the
> object in place on the stack. Similar to "*Placement new*":
> http://en.wikipedia.org/wiki/Placement_syntax
> - *RVO* (Return Value Optimization):
> http://en.wikipedia.org/wiki/Return_value_optimization
> - *NRVO* (Named Return Value Optimization):
> https://msdn.microsoft.com/en-us/library/ms364057%28v=vs.80%29.aspx
> - Since modern compilers perform these optimizations, we no longer need to
> 'hint' to the compiler that the copies can be elided.
> h3. Example program
> {code}
> #include <stdio.h>
> class T {
> public:
> T(const char* str) : Str(str) {
> printf("+ T(%s)\n", Str);
> }
> ~T() {
> printf("- T(%s)\n", Str);
> }
> const T& Member() const
> {
> return *this;
> }
> private:
> const char* Str;
> };
> T f(const char* s) { return T(s); }
> int main() {
> const T& good = T("Ok");
> const T& good_f = f("Ok function");
> const T& bad = T("Bad!").Member();
> const T& bad_f = T("Bad function!").Member();
> printf("End of function scope...\n");
> }
> {code}
> Output:
> {code}
> + T(Ok)
> + T(Ok function)
> + T(Bad!)
> - T(Bad!)
> + T(Bad function!)
> - T(Bad function!)
> End of function scope...
> - T(Ok function)
> - T(Ok)
> {code}
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)