May 30th, 2024

C++ Refactoring tools in Visual Studio

Mryam Girmay
Program Manager

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.

Gif showing a Rename example.

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.

Image AIPoweredRename example

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. 

Gif displaying Change Signature example.

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. 

Gif displaying an example of Extract 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. 

Gif displaying an example of Implement Pure Virtuals.

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.  

Gif displaying an example of Create Declaration / Definition.

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. 

Gif displaying an example of Move Function Definition.

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.

Gif displaying an example of Raw String Literal.

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;. 

Gif showing an example of Convert Macro to Constexpr.

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.

Gif displaying an example of refactoring using Copilot.

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. 

 

 

Category
C++

Author

Mryam Girmay
Program Manager

1 comment

Discussion is closed. Login to edit/delete existing comments.

  • Georgi Hadzhigeorgiev

    Very nice indeed!