Reputation: 46546
Suppose I have a 5000-line-long, deeply nested function, and I want to extract a 1000-line block into a new function.
In Java and C# I can let ReSharper, IntelliJ, and Visual C# handle the analysis required to safely extract a method, no matter how long and gnarly the code is. I can be confident that they won't change the behavior of the code, even if it's too complex for me to understand with my small brain.
The available C++ tools are not able to give me the same level of confidence. CLion, ReSharper, and Visual Assist will all introduce behavior changes when they extract a method.
What are my options?
Upvotes: 3
Views: 1687
Reputation: 46546
One option is to use this recipe, based on Tennent's Correspondence Principle. You may apply it to a whole block (surrounded by braces) or to an if
, while
, or for
statement (which create their own scopes).
Surround the block in question with:
[&]() {
// original code
}();
Compile the file. Possible errors:
not all control paths return a value. You have an early return. Back up and either Eliminate Early Return/Continue/Break or extract something different.
a break/continue statement may only be used within... You have a break/continue. Back up and either Eliminate Early Return/Continue/Break or extract something different.
Check the new lambda for any return statements. If there are any returns and it's obvious that all code paths return, then add a return statement on the next line after the lambda. If there are any returns and it's not obvious that all code paths return, then back up and either Eliminate Early Return/Continue/Break or try extracting something different.
i.e.
[&]() {
// ...
}();
becomes:
auto Applesauce = [&]() {
// ...
};
Applesauce();
Compile to make sure you didn't typo.
Set the return type on the lambda (even if it's void
). In Visual Studio, the tooltip over auto
will tell you the type.
i.e.:
auto Applesauce = [&]() -> SOMETYPE {
// ...
};
Compile to make sure you got the return type correct.
Replace [&]
with [this]
(or []
in a free function) and compile.
For each error about a variable that must be captured:
- Copy the variable name
- Paste it in to the capture list, prefixed with &
- Repeat until green.
i.e.:
auto Applesauce = [this, &foo]() -> void {
cout << foo;
};
The order of the capture list will influence the order of the parameters of the final function. If you want the parameters in a particular order, now is a good time to reorder the capture list.
For each captured local variable (except this
)
- Go to the definition of the variable
- Copy the variable declaration (e.g. Column* pCol
)
- Paste in to the lambda parameter list
- Make the parameter const and by-reference
- Remove the variable from the capture list
- Pass the variable in to the call
- Compile.
i.e.:
Column* pCol = ...
auto Applesauce = [&pCol]() -> void { cout << pCol->name(); };
Applesauce();
becomes
Column* pCol = ...
auto Applesauce = [](Column*& pCol) -> void { cout << pCol->name(); };
Applesauce(pCol);
If this
is captured, use 6A.
If this
is not captured, use 6B.
= [this]
SomeClass::
i.e.:
auto SomeClass::Applesauce () const -> void {
// ...
};
= []
Upvotes: 6