modern-cpp-refactor
Modern C++ refactoring intelligence. Scans and modernizes legacy C/C++98/11 code to C++17/20 standards. Detects: raw pointers, manual memory, C-style arrays, old containers, manual loops. Replaces with: smart pointers, RAII, std::array/vector, ranges, std::format, structured bindings, auto, constexpr, concepts, std::optional, std::variant. Actions: scan, analyze, refactor, modernize, upgrade, review, check C++ code. Patterns: memory leaks, null checks, iterator loops, string formatting, error handling, ownership semantics.
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
Modern C++ Refactor - Code Modernization Intelligence
Automated detection and refactoring of legacy C/C++98/11 code to modern C++17/20 standards using smart pointers, ranges, std::format, and contemporary best practices.
Prerequisites
Check if Python is installed:
python3 --version || python --version
If Python is not installed, install it based on user's OS:
macOS:
brew install python3
Ubuntu/Debian:
sudo apt update && sudo apt install python3
Windows:
winget install Python.Python.3.12
How to Use This Skill
When user requests C++ code refactoring, modernization, or asks to write new C++ code, follow this workflow:
Step 0: Check Project Coding Standards (CRITICAL)
ALWAYS check for .clang-format file first:
# Check if .clang-format exists in project
ls .clang-format || find . -name ".clang-format" -type f
If .clang-format exists:
- STRICTLY FOLLOW the formatting rules defined in it
- Do NOT deviate from the project's established style
- Use the same brace style, indentation, spacing as configured
- After refactoring, format code with:
clang-format -i <file>
If .clang-format does NOT exist:
- Follow Modern C++ best practices from this guide
- Consider creating a
.clang-formatfor consistency
Also check for other project standards:
-
CMakeLists.txtorMakefilefor C++ standard version (C++17/20/23) -
CONTRIBUTING.mdorSTYLE.mdfor coding guidelines - Existing code patterns in the codebase
Step 1: Scan for Legacy Patterns
Use the scanner script to detect legacy C++ patterns:
python3 .claude/skills/modern-cpp-refactor/scripts/scan.py <file_or_directory> [--detailed]
This will identify:
- Raw pointers and manual memory management
- C-style arrays and strings
- Old-style casts
- Manual loops vs ranges
- Printf/sprintf vs std::format
- NULL vs nullptr
- typedef vs using
Step 2: Analyze Code Context
Before refactoring, understand:
- C++ Standard: What standard is currently used? (Check CMakeLists.txt, Makefile, compiler flags)
- Dependencies: Are there third-party libraries that require specific patterns?
- Ownership: Who owns the memory? (unique_ptr, shared_ptr, or value semantics?)
- Performance: Is this hot path code? (Consider move semantics, perfect forwarding)
Step 3: Apply Modern C++ Transformations
Follow the modernization rules below to refactor code systematically.
Modern C++ Transformation Rules
Memory Management
| Legacy Pattern | Modern C++17/20 | When to Use |
|---|---|---|
new / delete |
std::make_unique<T>() |
Exclusive ownership |
new / delete |
std::make_shared<T>() |
Shared ownership |
| Raw pointer parameters | T& or std::span<T> |
Non-owning access |
new T[size] |
std::vector<T> or std::array<T, N> |
Dynamic/fixed arrays |
Manual delete[] |
Automatic via RAII containers | Always |
malloc / free |
std::vector or smart pointers |
C++ code (never use malloc) |
Example:
// Legacy
int* data = new int[100];
// ... use data ...
delete[] data;
// Modern C++17/20
auto data = std::vector<int>(100);
// ... use data ... (automatic cleanup)
// Or for fixed size:
auto data = std::array<int, 100>{};
Raw Pointers to Smart Pointers
| Legacy Pattern | Modern C++17/20 | Reasoning |
|---|---|---|
Widget* ptr = new Widget(); |
auto ptr = std::make_unique<Widget>(); |
Exception-safe, RAII |
Function returns T* (ownership) |
Function returns std::unique_ptr<T> |
Clear ownership transfer |
Function accepts T* (ownership) |
Function accepts std::unique_ptr<T> |
Clear ownership transfer |
Function accepts T* (non-owning) |
Function accepts T& or T* (document!) |
Non-null use T& |
NULL |
nullptr |
Type-safe null |
Example:
// Legacy
class Manager {
Widget* widget;
public:
Manager() : widget(new Widget()) {}
~Manager() { delete widget; }
};
// Modern C++17/20
class Manager {
std::unique_ptr<Widget> widget;
public:
Manager() : widget(std::make_unique<Widget>()) {}
// No destructor needed - automatic cleanup
};
Containers and Arrays
| Legacy Pattern | Modern C++17/20 | Benefits |
|---|---|---|
T arr[N] |
std::array<T, N> |
Bounds checking, STL algorithms |
T* arr = new T[n] |
std::vector<T> |
RAII, dynamic sizing |
C-string char* |
std::string |
Memory-safe, easier manipulation |
char buffer[256] |
std::string or std::vector<char> |
No buffer overflow |
| Manual size tracking | .size() method |
Automatic |
Example:
// Legacy
char buffer[256];
sprintf(buffer, "Value: %d", value);
// Modern C++20
auto buffer = std::format("Value: {}", value);
// Modern C++17 (without format)
auto buffer = std::string("Value: ") + std::to_string(value);
Loops and Algorithms
| Legacy Pattern | Modern C++17/20 | Benefits |
|---|---|---|
for (int i = 0; i < n; i++) |
for (auto& elem : container) |
Cleaner, less error-prone |
| Iterator loops | Range-based for or ranges | More expressive |
Manual find loop |
std::find, std::find_if |
Standard, optimized |
Manual sum loop |
std::accumulate or ranges |
Declarative intent |
Manual transform loop |
std::transform or ranges::transform |
Functional style |
Example:
// Legacy
std::vector<int> vec = {1, 2, 3, 4, 5};
for (size_t i = 0; i < vec.size(); i++) {
vec[i] *= 2;
}
// Modern C++17
for (auto& val : vec) {
val *= 2;
}
// Modern C++20 (ranges)
#include <ranges>
auto doubled = vec | std::views::transform([](int x) { return x * 2; });
Type Declarations
| Legacy Pattern | Modern C++17/20 | Benefits |
|---|---|---|
| Explicit types everywhere | auto for obvious types |
Less typing, easier refactoring |
typedef std::vector<int> IntVec |
using IntVec = std::vector<int> |
Consistent syntax |
| Type-specific functions | template<typename T> with concepts |
Type-safe generics |
std::pair with .first, .second |
Structured bindings auto [a, b] |
Named values |
Example:
// Legacy
std::map<std::string, int>::iterator it = myMap.find("key");
if (it != myMap.end()) {
int value = it->second;
}
// Modern C++17
if (auto it = myMap.find("key"); it != myMap.end()) {
auto [key, value] = *it;
// Use key and value directly
}
// Modern C++17 (structured binding)
for (const auto& [key, value] : myMap) {
// Use key and value directly
}
Error Handling
| Legacy Pattern | Modern C++17/20 | When to Use |
|---|---|---|
Return -1 or nullptr |
std::optional<T> |
Operation may fail |
| Exception + error code | std::expected<T, Error> (C++23) or std::optional |
Expected failures |
| Multiple return types | std::variant<T1, T2> |
Type-safe union |
| Boolean + out parameter | Return std::optional<T> |
Cleaner API |
Example:
// Legacy
Widget* findWidget(int id) {
// ... search ...
if (not_found) return nullptr;
return widget;
}
// Modern C++17
std::optional<Widget> findWidget(int id) {
// ... search ...
if (not_found) return std::nullopt;
return widget;
}
// Usage:
if (auto widget = findWidget(42)) {
// Use *widget
}
String Formatting
| Legacy Pattern | Modern C++17/20 | Benefits |
|---|---|---|
sprintf, snprintf |
std::format (C++20) |
Type-safe, no buffer overflow |
std::stringstream |
std::format |
Simpler, faster |
| Manual string concat | std::format or fmt::format |
Readable |
Example:
// Legacy
char buffer[256];
sprintf(buffer, "Name: %s, Age: %d", name.c_str(), age);
// Modern C++20
auto result = std::format("Name: {}, Age: {}", name, age);
// Alternative (C++17 with fmt library):
auto result = fmt::format("Name: {}, Age: {}", name, age);
Casts
| Legacy Pattern | Modern C++17/20 | Safety |
|---|---|---|
C-style cast (Type)value |
static_cast<Type>(value) |
Explicit intent |
(Type)ptr for pointers |
dynamic_cast<Type*>(ptr) |
Runtime type check |
Const removal (Type) |
const_cast<Type>(value) |
Explicit const violation |
reinterpret_cast |
Avoid or document heavily | Dangerous |
Initialization
| Legacy Pattern | Modern C++17/20 | Benefits |
|---|---|---|
Type var = Type(args) |
Type var{args} |
Uniform initialization |
Type var; (uninitialized) |
Type var{} |
Zero-initialized |
| Copy initialization | Direct initialization {} |
No implicit conversions |
Example:
// Legacy
std::vector<int> vec = std::vector<int>(10, 5);
// Modern C++17
auto vec = std::vector<int>(10, 5);
// Or even better:
std::vector<int> vec(10, 5);
Const Correctness
| Rule | Modern C++ Standard |
|---|---|
Mark functions const if they don't modify |
Always |
Use const& for read-only parameters |
For non-primitive types |
Use constexpr for compile-time constants |
C++17/20 |
Use if constexpr for compile-time branches |
C++17 |
Example:
// Legacy
int getValue() { return value; }
// Modern C++17
int getValue() const { return value; }
constexpr int getMax() const { return 100; }
Move Semantics
| Legacy Pattern | Modern C++17/20 | Benefits |
|---|---|---|
| Pass by value (copy) | Pass by const& |
No unnecessary copy |
| Return large objects | Return by value (RVO/NRVO) | Compiler optimization |
| Explicit copy | std::move for transfer |
Efficient |
Example:
// Modern C++17
class Widget {
std::vector<int> data;
public:
// Move constructor
Widget(Widget&& other) noexcept
: data(std::move(other.data)) {}
// Move assignment
Widget& operator=(Widget&& other) noexcept {
data = std::move(other.data);
return *this;
}
};
Concepts (C++20)
| Legacy Pattern | Modern C++20 | Benefits |
|---|---|---|
| Template + SFINAE | Concepts | Clear constraints, better errors |
typename everywhere |
Concepts with semantic names | Self-documenting |
Example:
// Legacy
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
add(T a, T b) { return a + b; }
// Modern C++20
template<std::integral T>
T add(T a, T b) { return a + b; }
// Or with concept:
std::integral auto add(std::integral auto a, std::integral auto b) {
return a + b;
}
Code Scanning Categories
The scanner will check for these categories:
1. Memory Safety Issues
- Raw
new/delete - Manual array allocation
- Missing
delete(potential leaks) -
malloc/freein C++ code
2. Type Safety Issues
- C-style casts
-
NULLinstead ofnullptr - Missing
constqualifiers - Implicit conversions
3. Modernization Opportunities
-
typedefinstead ofusing - Manual loops vs ranges
-
std::bindvs lambdas - Raw function pointers vs
std::function
4. String and I/O Issues
- C-style strings (
char*,char[]) -
sprintffamily -
printfvsstd::format
5. Concurrency (if applicable)
- Raw
std::threadwithout RAII wrapper - Manual locking vs
std::lock_guard - Missing
std::atomicfor shared data
Pre-Refactor Checklist
Before modernizing code, verify:
- Identify the target C++ standard (C++17 or C++20)
- Check if compiler supports required features
- Understand existing ownership semantics
- Identify performance-critical sections
- Check for third-party library constraints
- Ensure comprehensive test coverage exists
- Plan incremental refactoring (not all at once)
Post-Refactor Checklist
After modernizing code, verify:
- All tests still pass
- No memory leaks (use valgrind/sanitizers)
- Performance is acceptable (profile if needed)
- Code compiles with
-Wall -Wextra -Werror - Smart pointers have correct ownership semantics
- Move semantics applied where beneficial
-
constcorrectness maintained - No raw
new/deleteremaining (except special cases)
Example Workflow
User request: "Modernize this C++ class to use C++17/20 features"
AI should:
- Scan the code:
python3 .claude/skills/modern-cpp-refactor/scripts/scan.py src/MyClass.cpp --detailed
-
Analyze findings:
- Identify legacy patterns (raw pointers, manual loops, etc.)
- Understand ownership semantics
- Check for performance implications
-
Refactor systematically:
- Replace raw pointers with smart pointers
- Convert manual loops to range-based or algorithms
- Use
autofor obvious types - Apply structured bindings where applicable
- Replace
sprintfwithstd::format(C++20) or alternatives - Add
constexprandconstwhere appropriate
-
Verify:
- Compile with modern C++ flags
- Run tests
- Check for memory leaks
- Review performance
Common Anti-Patterns to Avoid
Don't Over-Use Smart Pointers
// Bad: Using unique_ptr for simple value
std::unique_ptr<int> value = std::make_unique<int>(42);
// Good: Just use a value
int value = 42;
Don't Use Shared Ptr Everywhere
// Bad: Shared ownership without need
std::shared_ptr<Widget> widget = std::make_shared<Widget>();
// Good: Unique ownership when possible
auto widget = std::make_unique<Widget>();
Don't Ignore Move Semantics
// Bad: Unnecessary copy
std::vector<int> getData() {
std::vector<int> result = computeData();
return result; // Copy
}
// Good: Move semantics (or RVO)
std::vector<int> getData() {
return computeData(); // Move or RVO
}
Quick Reference: Modern C++ Features by Standard
C++17 Must-Use Features
- Structured bindings:
auto [a, b] = pair; -
if/switchwith initializer:if (auto x = foo(); x > 0) -
std::optional<T>for maybe-values -
std::variant<T1, T2>for type-safe unions -
std::string_viewfor non-owning string refs - Fold expressions in templates
- Class template argument deduction (CTAD)
C++20 Must-Use Features
-
std::formatfor string formatting - Ranges and views
- Concepts for template constraints
-
std::span<T>for array views - Three-way comparison operator
<=> - Designated initializers:
Point{.x=1, .y=2} -
constevalfor compile-time evaluation
When NOT to Modernize
Sometimes legacy patterns are necessary:
- Interfacing with C APIs: May require raw pointers
- Performance-critical code: Profile before/after
- Third-party library constraints: API may require specific types
- Incremental migration: Don't refactor everything at once
- Platform limitations: Embedded systems may lack std library
Additional Resources
Default Behavior
When user asks you to write NEW C++ code (not just refactor):
STEP 0 - ALWAYS CHECK FIRST:
-
Check for
.clang-formatfile - If it exists, STRICTLY follow its formatting rules
THEN follow these modern C++ standards:
- Always use C++17/20 standards by default
-
Never use raw
new/delete- use smart pointers or RAII containers -
Prefer
std::format(C++20) orfmtlibrary oversprintf -
Use
autofor obvious types - Use range-based for instead of manual index loops
-
Use
std::optionalfor maybe-values - Use structured bindings for pairs/tuples/structs
-
Mark functions
constandconstexprwhen applicable -
Use
nullptrneverNULL -
Use
usingnevertypedef -
Respect project's
.clang-formatfor all code formatting decisions
This ensures all generated code follows both modern C++ best practices AND the project's established coding standards.
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-03-11
- Mise à jour de l’index
- 2026-03-11
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!