| name | modern-cpp-refactor |
| description | 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. |
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:
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-format for consistency
Also check for other project standards:
CMakeLists.txt or Makefile for C++ standard version (C++17/20/23)
CONTRIBUTING.md or STYLE.md for 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:
int* data = new int[100];
delete[] data;
auto data = std::vector<int>(100);
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:
class Manager {
Widget* widget;
public:
Manager() : widget(new Widget()) {}
~Manager() { delete widget; }
};
class Manager {
std::unique_ptr<Widget> widget;
public:
Manager() : widget(std::make_unique<Widget>()) {}
};
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:
char buffer[256];
sprintf(buffer, "Value: %d", value);
auto buffer = std::format("Value: {}", value);
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:
std::vector<int> vec = {1, 2, 3, 4, 5};
for (size_t i = 0; i < vec.size(); i++) {
vec[i] *= 2;
}
for (auto& val : vec) {
val *= 2;
}
#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:
std::map<std::string, int>::iterator it = myMap.find("key");
if (it != myMap.end()) {
int value = it->second;
}
if (auto it = myMap.find("key"); it != myMap.end()) {
auto [key, value] = *it;
}
for (const auto& [key, value] : myMap) {
}
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:
Widget* findWidget(int id) {
if (not_found) return nullptr;
return widget;
}
std::optional<Widget> findWidget(int id) {
if (not_found) return std::nullopt;
return widget;
}
if (auto widget = findWidget(42)) {
}
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:
char buffer[256];
sprintf(buffer, "Name: %s, Age: %d", name.c_str(), age);
auto result = std::format("Name: {}, Age: {}", name, age);
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:
std::vector<int> vec = std::vector<int>(10, 5);
auto vec = std::vector<int>(10, 5);
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:
int getValue() { return value; }
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:
class Widget {
std::vector<int> data;
public:
Widget(Widget&& other) noexcept
: data(std::move(other.data)) {}
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:
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
add(T a, T b) { return a + b; }
template<std::integral T>
T add(T a, T b) { return a + b; }
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 / free in C++ code
2. Type Safety Issues
- C-style casts
NULL instead of nullptr
- Missing
const qualifiers
- Implicit conversions
3. Modernization Opportunities
typedef instead of using
- Manual loops vs ranges
std::bind vs lambdas
- Raw function pointers vs
std::function
4. String and I/O Issues
- C-style strings (
char*, char[])
sprintf family
printf vs std::format
5. Concurrency (if applicable)
- Raw
std::thread without RAII wrapper
- Manual locking vs
std::lock_guard
- Missing
std::atomic for shared data
Pre-Refactor Checklist
Before modernizing code, verify:
Post-Refactor Checklist
After modernizing code, verify:
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
auto for obvious types
- Apply structured bindings where applicable
- Replace
sprintf with std::format (C++20) or alternatives
- Add
constexpr and const where 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
std::unique_ptr<int> value = std::make_unique<int>(42);
int value = 42;
Don't Use Shared Ptr Everywhere
std::shared_ptr<Widget> widget = std::make_shared<Widget>();
auto widget = std::make_unique<Widget>();
Don't Ignore Move Semantics
std::vector<int> getData() {
std::vector<int> result = computeData();
return result;
}
std::vector<int> getData() {
return computeData();
}
Quick Reference: Modern C++ Features by Standard
C++17 Must-Use Features
- Structured bindings:
auto [a, b] = pair;
if / switch with initializer: if (auto x = foo(); x > 0)
std::optional<T> for maybe-values
std::variant<T1, T2> for type-safe unions
std::string_view for non-owning string refs
- Fold expressions in templates
- Class template argument deduction (CTAD)
C++20 Must-Use Features
std::format for 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}
consteval for 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-format file - 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) or fmt library over sprintf
- Use
auto for obvious types
- Use range-based for instead of manual index loops
- Use
std::optional for maybe-values
- Use structured bindings for pairs/tuples/structs
- Mark functions
const and constexpr when applicable
- Use
nullptr never NULL
- Use
using never typedef
- Respect project's
.clang-format for all code formatting decisions
This ensures all generated code follows both modern C++ best practices AND the project's established coding standards.