Code refactoring is the process of restructuring existing code, while its outward functionality remains unchanged. By refactoring, you can simplify complex code structures, reduce code redundancy, and enhance code reusability. Visual Studio offers many tools to help refactor your C++ code. This article will delve into these tools, using the bullet3 open-source code as an example.
Rename
The renaming tool is useful when changing a symbol name that appears in multiple locations within your code. Manual renaming can lead to errors if one or more instances of your function or variable are overlooked. However, the renaming tool ensures all instances are renamed accurately and seamlessly.
Consider the ExampleEntryPhysicsServer
struct in the ‘InProcessExampleBrowser.cpp’ file. Suppose we want to rename the m_menuLevel
variable to m_menuHierarchyLevel
to make it more descriptive for clarity. Visual Studio’s Rename refactoring tool can accomplish this, by right-clicking on the variable name, selecting Rename, and then typing the new name. Visual Studio will automatically update all instances of m_menuLevel
to m_menuHierarchyLevel
, eliminating the need for manual renaming.
AI-Powered Rename Suggestions
GitHub Copilot‘ s AI-Powered Rename Suggestions is a new feature in Visual Studio 2022 that generates dynamic name suggestions for any C++ identifier, aiding in achieving a balance between descriptiveness and brevity. This allows you to dedicate your efforts towards evaluating the proposed names, rather than generating new ones.
You can make this feature visible by selecting “Enable rename suggestions” Tools > Options > GitHub > Copilot chat > Preview features.
Let’s use the AI-Powered Rename Suggestions to rename m_name
to a more descriptive one. First, right-click on the variable and choose Rename. In the Rename window that appears, click on the new sparkle icon to generate name suggestions. Copilot chat will then offer suggestions based on how the identifier is used and the naming rules in your code. Let’s select m_entryName
, and then preview and apply the change.
Change Signature
The Change Signature feature helps you modify a function’s parameters in a safe and efficient manner. When we want to provide a new parameter for a description to the first instance of ExampleEntryPhysicsServer
constructor, we will right-click on its name and select the ‘Quick Actions and Refactorings…’ option or use the Ctrl+. keyboard shortcut. Select the Change Signature option to open a new window. Here, you can add, remove, or reorder parameters. We’ll add a constant string pointer m_description
, by typing the type and name. Visual Studio will automatically update all function calls accordingly.
Similar to renaming, changing functions one by one can result in errors and this tool can do it automatically to eliminate those errors.
Extract Function
Extract Function is a great way to break down complex functions into smaller, more manageable parts, and allows for potential reusability of a block of code if similar functionality is needed elsewhere in the program. In this code, let’s extract the block of code from the ExampleBrowserThreadFunc
function that calculates and updates deltaTimeInSeconds
and then decides whether to update graphics or reset the clock based on deltaTimeInSeconds
. This block of code is a logical unit that performs a specific task, so it makes sense to extract it into a separate function.
To do so, select the block of code that we want to extract, then right-click on the editor, then select the ‘Quick Actions and Refactorings…’ option. This will display the Extract Function tool. Choosing that option will open a new window that will allow you to choose a new function name and placement. A new function will be set up at the chosen location, with a matching prototype in the header file. The initial code will be revised to invoke this function.
This helps to avoid code duplication and makes your code easier to read and maintain.
Implement Pure Virtuals
When dealing with a base class that has pure virtual functions, Visual Studio can help implement these functions in all derived classes. In this source code, all pure virtual functions of the base class are implemented. However, for the sake of an example, I modified the CommonRigidBodyBase
struct to include a pure virtual function virtual void somePureVirtualFunction () = 0;
. Now we will use Implement Pure Virtuals to provide an implementation for somePureVirtualFunction ()
in the BasicExample
class.
First, right-click on the derived class and select the ‘Quick Actions and Refactorings…’ option from the context menu or use Ctrl+. keyboard shortcut. Then select the Implement all Pure Virtuals for class BasicExample
option, and the pure virtual method will be added automatically.
This saves more time especially if you have a lot of virtual functions, because instead of doing it one by one, this tool will implement all pure virtual functions automatically.
Create Declaration / Definition
This feature enables you to easily generate a function’s declaration or definition. Let’s consider adding a new function, printExampleDetails
that takes a pointer to an ExampleEntriesPhysicsServer
object and prints the details of all the examples registered in the server. We can use Create Declaration/Definition tool, to seamlessly declare it.
Select the block of code, then right-click on the editor, then select the ’Quick Actions and Refactorings…’ option from the context menu or use Ctrl+. keyboard shortcut. This will display the Create Declaration/Definition option, which will automatically create the declaration for that function.
Move Definition Location
Now that we have seen how to declare a function, we can move a function’s definition to its corresponding header file. In the ‘ExampleEntries.cpp’ file, we have a ExampleEntriesAll::registerExampleEntry
function. For code reusability, let’s move the definition of this function to a header file.
To move function location, right-click on the function, then select the ‘Quick Actions and Refactorings…’ option from the context menu or use Ctrl+. keyboard shortcut. Then choose the Move Definition Location option, to move the function to the corresponding header file.
This feature can be very useful when you want to organize your code by moving function definitions to appropriate header files without having to do it manually.
Convert to Raw String Literal
Next, let’s look at a string with special characters or escape sequences. For example, if you have a string Torus (Shape Match)
and you want to add quotation marks around Torus
, you would normally need to use escape characters, like this: \"Torus\" (Shape Match)
. However, this can make the string harder to read, especially if there are many special characters. To solve this problem, we can convert them to raw string literals by prefixing the string with R
and enclosing it in parentheses, like this: R"("Torus" (Shape Match))"
. This tells the program to interpret all characters within the parentheses as literal characters, not special characters.
To convert to Raw String Literal, right-click on the string and then select the ‘Quick Actions and Refactorings…’ option from the context menu or use the Ctrl+. keyboard shortcut. Then, selecting the ‘Convert to Raw String Literal’ option will complete the process. Visual Studio will preserve the exact content of the string.
Convert Macros to Constexpr
Macros can be challenging to read and debug but converting them to constexpr functions can enhance code readability and maintainability. This feature identifies macros that can be converted to constexpr, and then offers a quick way to convert them to modern C++ constexpr expressions.
To convert #define ARRAY_SIZE_Y 5
to a constexpr, you can hover over the suggestion indicator (…) below the macro or select the macro and then chose ’Quick Actions and Refactorings…’ option from the context menu (Ctrl+. keyboard shortcut). This will display the Convert Macros to Constexpr option, which will complete the conversion to constexpr auto ARRAY_SIZE_Y = 5;
.
This feature can process constants and basic expressions contained in function-like macros. Due to performance optimization, some macros may not display the ‘…’ indicator, however, the conversion option remains readily available in the lightbulb menu. To configure Convert Macros to Constexpr, navigate Tools > Options > Text Editor > C/C++ > View > Macros Convertible to constexpr.
Convert Macros to Constexpr is a feature within our fix-it capabilities. In our forthcoming blog post, we will delve into additional fix-it features that are currently available in Visual Studio.
GitHub Copilot for Refactoring
GitHub Copilot, an AI-powered coding assistant, can help C++ developers refactor their code. It offers a streamlined process for refactoring, making it an efficient and effective tool for code optimization.
To utilize GitHub Copilot for refactoring, begin by identifying the segment of code you wish to refactor. This could be a function, a class, or even a single line of code. Once identified, right-click on the selected code and request assistance from Copilot to refactor it. The command can be tailored to your specific needs. Upon generating a suggestion for the refactoring, it’s crucial to review this carefully. Make any necessary adjustments to ensure it aligns with your coding standards and project requirements. Once you’re satisfied with the proposed refactoring, proceed to implement the changes in your code.
For instance, suppose you’re working with a complex function in C++, like btCreateInProcessExampleBrowser
in ‘InProcessExampleBrowser.cpp’. To simplify it, you’d highlight the function and ask GitHub Copilot for a simplification. Copilot suggested using auto for type inference, nullptr instead of 0, static_cast for safer type conversion, and limiting the scope of loop variables. These changes improve readability, safety, and maintainability. You can select preview, to compare and review the differences between the original and proposed code. If you’re satisfied with the changes, you can accept them to incorporate the refactored code directly into your existing codebase.
Send us your feedback
We encourage you to continue exploring the C++ refactoring features in Visual Studio. Your insights and experiences are invaluable to us, therefore, please share your feedback and suggestions in the comments section below or through the Developer Community. You can also reach out to us on Twitter (@VisualC) or via email at visualcpp@microsoft.com. We are always eager to hear from you.
Furthermore, if there are additional refactoring features that you find necessary or would frequently use, we would greatly appreciate your input. Your suggestions help us understand your needs better and continually improve our product.
Very nice indeed!