@polderlabs/bizar 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +364 -0
- package/cli/audit.mjs +144 -0
- package/cli/banner.mjs +41 -0
- package/cli/bin.mjs +186 -0
- package/cli/copy.mjs +508 -0
- package/cli/export.mjs +87 -0
- package/cli/init.mjs +147 -0
- package/cli/install.mjs +390 -0
- package/cli/plan-templates.mjs +523 -0
- package/cli/plan.mjs +2087 -0
- package/cli/prompts.mjs +163 -0
- package/cli/update.mjs +273 -0
- package/cli/utils.mjs +153 -0
- package/config/AGENTS.md +282 -0
- package/config/agents/baldr.md +148 -0
- package/config/agents/forseti.md +112 -0
- package/config/agents/frigg.md +101 -0
- package/config/agents/heimdall.md +157 -0
- package/config/agents/hermod.md +144 -0
- package/config/agents/mimir.md +115 -0
- package/config/agents/odin.md +309 -0
- package/config/agents/quick.md +78 -0
- package/config/agents/semble-search.md +44 -0
- package/config/agents/thor.md +97 -0
- package/config/agents/tyr.md +96 -0
- package/config/agents/vidarr.md +100 -0
- package/config/agents/vor.md +140 -0
- package/config/commands/audit.md +1 -0
- package/config/commands/explain.md +1 -0
- package/config/commands/init.md +1 -0
- package/config/commands/learn.md +1 -0
- package/config/commands/pr-review.md +1 -0
- package/config/commands/tailscale-serve.md +96 -0
- package/config/hooks/README.md +29 -0
- package/config/hooks/post-tool-use.md +16 -0
- package/config/hooks/pre-tool-use.md +16 -0
- package/config/opencode.json +52 -0
- package/config/opencode.json.template +52 -0
- package/config/rules/general.md +8 -0
- package/config/rules/git.md +11 -0
- package/config/rules/javascript.md +10 -0
- package/config/rules/python.md +10 -0
- package/config/rules/testing.md +10 -0
- package/config/skills/bizar/README.md +9 -0
- package/config/skills/bizar/SKILL.md +187 -0
- package/config/skills/cpp-coding-standards/README.md +28 -0
- package/config/skills/cpp-coding-standards/SKILL.md +634 -0
- package/config/skills/cpp-coding-standards/agents/openai.yaml +4 -0
- package/config/skills/cpp-coding-standards/references/concurrency.md +320 -0
- package/config/skills/cpp-coding-standards/references/error-handling.md +229 -0
- package/config/skills/cpp-coding-standards/references/memory-safety.md +216 -0
- package/config/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
- package/config/skills/cpp-coding-standards/references/review-checklist.md +96 -0
- package/config/skills/cpp-testing/README.md +28 -0
- package/config/skills/cpp-testing/SKILL.md +304 -0
- package/config/skills/cpp-testing/agents/openai.yaml +4 -0
- package/config/skills/cpp-testing/references/coverage.md +370 -0
- package/config/skills/cpp-testing/references/framework-compare.md +175 -0
- package/config/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
- package/config/skills/cpp-testing/references/mocking.md +364 -0
- package/config/skills/cpp-testing/references/tdd-workflow.md +308 -0
- package/config/skills/embedded-esp-idf/README.md +41 -0
- package/config/skills/embedded-esp-idf/SKILL.md +439 -0
- package/config/skills/embedded-esp-idf/agents/openai.yaml +4 -0
- package/config/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
- package/config/skills/embedded-esp-idf/references/host-tests.md +164 -0
- package/config/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
- package/config/skills/embedded-esp-idf/references/kconfig.md +159 -0
- package/config/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
- package/config/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
- package/config/skills/embedded-esp-idf/references/nvs.md +121 -0
- package/config/skills/embedded-esp-idf/references/packed-structs.md +192 -0
- package/config/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
- package/config/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
- package/config/skills/self-improvement/SKILL.md +64 -0
- package/package.json +47 -0
- package/templates/plan/htmx.min.js +1 -0
- package/templates/plan/library/bug-investigation.mdx +79 -0
- package/templates/plan/library/decision-record.mdx +71 -0
- package/templates/plan/library/feature-design.mdx +92 -0
- package/templates/plan/meta.json.template +8 -0
- package/templates/plan/plan.canvas.template +1711 -0
- package/templates/plan/plan.html.template +937 -0
- package/templates/plan/plan.mdx.template +46 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Memory Safety in Modern C++
|
|
2
|
+
|
|
3
|
+
This reference covers RAII, smart pointers, and the Rule of Five/Zero.
|
|
4
|
+
|
|
5
|
+
## RAII — Resource Acquisition Is Initialization
|
|
6
|
+
|
|
7
|
+
Every resource (heap memory, file handle, mutex, socket, etc.) must be owned by an object whose destructor performs cleanup. This guarantees cleanup even when exceptions are thrown.
|
|
8
|
+
|
|
9
|
+
```cpp
|
|
10
|
+
// GOOD — file closes automatically at scope exit
|
|
11
|
+
void read_file(const std::string& path) {
|
|
12
|
+
std::ifstream in(path);
|
|
13
|
+
std::string line;
|
|
14
|
+
while (std::getline(in, line)) {
|
|
15
|
+
process(line);
|
|
16
|
+
}
|
|
17
|
+
} // destructor closes file, no matter how we exit
|
|
18
|
+
|
|
19
|
+
// BAD — manual cleanup, error-prone
|
|
20
|
+
void read_file_bad(const std::string& path) {
|
|
21
|
+
std::ifstream* in = new std::ifstream(path);
|
|
22
|
+
std::string line;
|
|
23
|
+
while (std::getline(*in, line)) { // throws? leaks *in
|
|
24
|
+
process(line);
|
|
25
|
+
}
|
|
26
|
+
delete in;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Common RAII Types in the Standard Library
|
|
31
|
+
|
|
32
|
+
| Type | Resource |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `std::unique_ptr` | Heap memory (single owner) |
|
|
35
|
+
| `std::shared_ptr` | Heap memory (shared owner) |
|
|
36
|
+
| `std::ifstream` / `std::ofstream` | File handles |
|
|
37
|
+
| `std::lock_guard` / `std::scoped_lock` | Mutex lock |
|
|
38
|
+
| `std::jthread` (C++20) | Thread join on destruction |
|
|
39
|
+
|
|
40
|
+
## Smart Pointers
|
|
41
|
+
|
|
42
|
+
### `std::unique_ptr<T>`
|
|
43
|
+
|
|
44
|
+
Exclusive ownership. The default choice. Zero overhead over raw pointer.
|
|
45
|
+
|
|
46
|
+
```cpp
|
|
47
|
+
#include <memory>
|
|
48
|
+
|
|
49
|
+
// Creation
|
|
50
|
+
auto p1 = std::make_unique<int>(42);
|
|
51
|
+
auto p2 = std::make_unique<std::vector<int>>(10, 0);
|
|
52
|
+
|
|
53
|
+
// Custom deleter (rare — only when type needs special cleanup)
|
|
54
|
+
auto file_deleter = [](FILE* f) { if (f) fclose(f); };
|
|
55
|
+
std::unique_ptr<FILE, decltype(file_deleter)> fp(fopen("data.txt", "r"), file_deleter);
|
|
56
|
+
|
|
57
|
+
// Transfer ownership
|
|
58
|
+
std::unique_ptr<int> source = std::make_unique<int>(1);
|
|
59
|
+
std::unique_ptr<int> dest = std::move(source); // source is now nullptr
|
|
60
|
+
assert(!source);
|
|
61
|
+
assert(*dest == 1);
|
|
62
|
+
|
|
63
|
+
// Release the raw pointer (rare — only for C interop)
|
|
64
|
+
int* raw = p1.release();
|
|
65
|
+
delete raw; // you now own it — avoid this
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `std::shared_ptr<T>`
|
|
69
|
+
|
|
70
|
+
Shared ownership via reference counting. Use **only** when ownership genuinely must be shared. Prefer `unique_ptr` otherwise.
|
|
71
|
+
|
|
72
|
+
```cpp
|
|
73
|
+
auto shared = std::make_shared<int>(100);
|
|
74
|
+
|
|
75
|
+
// Copy increases ref count
|
|
76
|
+
auto shared2 = shared; // ref count = 2
|
|
77
|
+
|
|
78
|
+
// shared_ptr can be created from unique_ptr
|
|
79
|
+
auto unique = std::make_unique<double>(3.14);
|
|
80
|
+
std::shared_ptr<double> from_unique = std::move(unique);
|
|
81
|
+
|
|
82
|
+
// Weak pointer — breaks cycles
|
|
83
|
+
std::weak_ptr<int> weak = shared;
|
|
84
|
+
if (auto locked = weak.lock()) {
|
|
85
|
+
// use *locked safely
|
|
86
|
+
} // locked goes out of scope, ref count unchanged
|
|
87
|
+
|
|
88
|
+
// Custom deleter — stored separately from the control block
|
|
89
|
+
auto deleter = [](FILE* f) { fclose(f); };
|
|
90
|
+
std::shared_ptr<FILE> f(fopen("x.txt", "r"), deleter);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Performance notes:**
|
|
94
|
+
- `shared_ptr` is twice the size of a raw pointer (control block + pointer)
|
|
95
|
+
- Atomic reference count increments/decrements on copy/destroy
|
|
96
|
+
- `make_shared` allocates the object and control block in one allocation (preferred)
|
|
97
|
+
|
|
98
|
+
### `std::weak_ptr<T>`
|
|
99
|
+
|
|
100
|
+
Non-owning observer of a `shared_ptr`. Use to break reference cycles (A owns B, B observes A without owning).
|
|
101
|
+
|
|
102
|
+
```cpp
|
|
103
|
+
class Observer;
|
|
104
|
+
class Subject {
|
|
105
|
+
std::vector<std::weak_ptr<Observer>> observers_;
|
|
106
|
+
public:
|
|
107
|
+
void notify() {
|
|
108
|
+
for (auto& w : observers_) {
|
|
109
|
+
if (auto o = w.lock()) o->on_update();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Rule of Five
|
|
116
|
+
|
|
117
|
+
If you define **any one** of the five special member functions below, you likely need to define **all five**:
|
|
118
|
+
|
|
119
|
+
1. Destructor
|
|
120
|
+
2. Copy constructor
|
|
121
|
+
3. Copy assignment operator
|
|
122
|
+
4. Move constructor
|
|
123
|
+
5. Move assignment operator
|
|
124
|
+
|
|
125
|
+
```cpp
|
|
126
|
+
class Buffer {
|
|
127
|
+
int* data_ = nullptr;
|
|
128
|
+
size_t size_ = 0;
|
|
129
|
+
public:
|
|
130
|
+
explicit Buffer(size_t n) : data_(new int[n]), size_(n) {}
|
|
131
|
+
|
|
132
|
+
~Buffer() { delete[] data_; }
|
|
133
|
+
|
|
134
|
+
// Copy constructor
|
|
135
|
+
Buffer(const Buffer& o) : data_(new int[o.size_]), size_(o.size_) {
|
|
136
|
+
std::copy(o.data_, o.data_ + o.size_, data_);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Copy assignment operator
|
|
140
|
+
Buffer& operator=(const Buffer& o) {
|
|
141
|
+
if (this != &o) {
|
|
142
|
+
delete[] data_;
|
|
143
|
+
data_ = new int[o.size_];
|
|
144
|
+
size_ = o.size_;
|
|
145
|
+
std::copy(o.data_, o.data_ + o.size_, data_);
|
|
146
|
+
}
|
|
147
|
+
return *this;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Move constructor
|
|
151
|
+
Buffer(Buffer&& o) noexcept : data_(std::exchange(o.data_, nullptr)), size_(std::exchange(o.size_, 0)) {}
|
|
152
|
+
|
|
153
|
+
// Move assignment operator
|
|
154
|
+
Buffer& operator=(Buffer&& o) noexcept {
|
|
155
|
+
if (this != &o) {
|
|
156
|
+
delete[] data_;
|
|
157
|
+
data_ = std::exchange(o.data_, nullptr);
|
|
158
|
+
size_ = std::exchange(o.size_, 0);
|
|
159
|
+
}
|
|
160
|
+
return *this;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Rule of Zero
|
|
166
|
+
|
|
167
|
+
Prefer **not** defining any special members and letting the compiler generate them correctly. Use standard library containers and smart pointers to manage resources.
|
|
168
|
+
|
|
169
|
+
```cpp
|
|
170
|
+
// Rule of Zero — no manual resource management needed
|
|
171
|
+
class Point {
|
|
172
|
+
double x_ = 0, y_ = 0;
|
|
173
|
+
public:
|
|
174
|
+
Point() = default;
|
|
175
|
+
Point(double x, double y) : x_(x), y_(y) {}
|
|
176
|
+
double distance_to(const Point& o) const {
|
|
177
|
+
double dx = x_ - o.x_, dy = y_ - o.y_;
|
|
178
|
+
return std::sqrt(dx*dx + dy*dy);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// Compiler-generated copy/move/dtor are all correct here
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Common Mistakes
|
|
185
|
+
|
|
186
|
+
```cpp
|
|
187
|
+
// WRONG: Shared ownership where unique ownership suffices
|
|
188
|
+
auto widget = std::make_shared<Widget>();
|
|
189
|
+
process(std::shared_ptr<Widget>(widget)); // unnecessary ref count bump
|
|
190
|
+
|
|
191
|
+
// WRONG: Memory leak via raw new in container
|
|
192
|
+
std::vector<int*> v;
|
|
193
|
+
v.push_back(new int(1)); // who deletes these?
|
|
194
|
+
|
|
195
|
+
// RIGHT: Smart pointers in containers
|
|
196
|
+
std::vector<std::unique_ptr<int>> v;
|
|
197
|
+
v.push_back(std::make_unique<int>(1)); // auto-deleted
|
|
198
|
+
|
|
199
|
+
// WRONG: Missing virtual destructor in base class
|
|
200
|
+
struct Base {
|
|
201
|
+
virtual void foo() {}
|
|
202
|
+
// Missing virtual destructor — deleting through base pointer is UB
|
|
203
|
+
};
|
|
204
|
+
struct Derived : Base {
|
|
205
|
+
std::vector<int> data;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
Base* b = new Derived;
|
|
209
|
+
delete b; // UB: Derived's destructor not called
|
|
210
|
+
|
|
211
|
+
// RIGHT: Virtual destructor
|
|
212
|
+
struct Base {
|
|
213
|
+
virtual ~Base() = default; // polymorphic bases need virtual dtor
|
|
214
|
+
virtual void foo() {}
|
|
215
|
+
};
|
|
216
|
+
```
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Modern C++17/20 Idioms
|
|
2
|
+
|
|
3
|
+
This reference covers the most impactful modern C++ features with minimal examples.
|
|
4
|
+
|
|
5
|
+
## Structured Bindings
|
|
6
|
+
|
|
7
|
+
Unpack tuples, pairs, structs, and arrays into named variables.
|
|
8
|
+
|
|
9
|
+
```cpp
|
|
10
|
+
#include <tuple>
|
|
11
|
+
#include <map>
|
|
12
|
+
|
|
13
|
+
// Pair/tuple
|
|
14
|
+
auto [key, value] = std::pair{1, std::string("hello")};
|
|
15
|
+
|
|
16
|
+
// Array
|
|
17
|
+
auto [a, b, c] = std::array{1, 2, 3};
|
|
18
|
+
|
|
19
|
+
// Struct — works if all members are public
|
|
20
|
+
struct Point { double x, y, z; };
|
|
21
|
+
Point p{1.0, 2.0, 3.0};
|
|
22
|
+
auto [px, py, pz] = p;
|
|
23
|
+
|
|
24
|
+
// Map iteration
|
|
25
|
+
std::map<std::string, int> ages{{"Alice", 30}, {"Bob", 25}};
|
|
26
|
+
for (const auto& [name, age] : ages) {
|
|
27
|
+
std::cout << name << ": " << age << '\n';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Multiple return values
|
|
31
|
+
std::tuple<int, std::string, double> compute() { return {42, "answer", 3.14}; }
|
|
32
|
+
auto [code, label, score] = compute();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## `if constexpr`
|
|
36
|
+
|
|
37
|
+
Compile-time conditional code execution — the unselected branch is not instantiated.
|
|
38
|
+
|
|
39
|
+
```cpp
|
|
40
|
+
#include <type_traits>
|
|
41
|
+
|
|
42
|
+
template <typename T>
|
|
43
|
+
auto inspect(const T& val) {
|
|
44
|
+
if constexpr (std::is_integral_v<T>) {
|
|
45
|
+
return "integer";
|
|
46
|
+
} else if constexpr (std::is_floating_point_v<T>) {
|
|
47
|
+
return "floating-point";
|
|
48
|
+
} else {
|
|
49
|
+
return "other";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Works where SFINAE would require complex enable_if
|
|
54
|
+
template <typename T>
|
|
55
|
+
T doubled(T x) {
|
|
56
|
+
if constexpr (std::is_integral_v<T>) {
|
|
57
|
+
return x + x; // valid only for integral T
|
|
58
|
+
} else {
|
|
59
|
+
return x * 2.0; // valid only for floating-point T
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## `std::optional`
|
|
65
|
+
|
|
66
|
+
Represents a value that may or may not be present. Alternative to pointer-returning APIs.
|
|
67
|
+
|
|
68
|
+
```cpp
|
|
69
|
+
#include <optional>
|
|
70
|
+
|
|
71
|
+
// Return optional
|
|
72
|
+
std::optional<int> find_index(const std::vector<int>& v, int target) {
|
|
73
|
+
for (auto it = v.begin(); it != v.end(); ++it) {
|
|
74
|
+
if (*it == target)
|
|
75
|
+
return {static_cast<int>(std::distance(v.begin(), it))};
|
|
76
|
+
}
|
|
77
|
+
return std::nullopt;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check and use
|
|
81
|
+
if (auto idx = find_index(vec, 42)) {
|
|
82
|
+
std::cout << "Found at " << *idx << '\n';
|
|
83
|
+
} else {
|
|
84
|
+
std::cout << "Not found\n";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Transform an optional without unwrapping
|
|
88
|
+
std::optional<std::string> name = /* ... */;
|
|
89
|
+
auto greeting = name.transform([](const std::string& n) { return "Hello, " + n; });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## `std::string_view`
|
|
93
|
+
|
|
94
|
+
Non-owning view into a string. Use for read-only string operations without copying.
|
|
95
|
+
|
|
96
|
+
```cpp
|
|
97
|
+
#include <string_view>
|
|
98
|
+
|
|
99
|
+
// Function takes a view — no ownership, no allocation
|
|
100
|
+
bool ends_with(std::string_view s, std::string_view suffix) {
|
|
101
|
+
return s.size() >= suffix.size() && s.substr(s.size() - suffix.size()) == suffix;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Calling with different string types — no conversion/copy
|
|
105
|
+
std::string s = "filename.txt";
|
|
106
|
+
std::string_view sv = s;
|
|
107
|
+
ends_with(sv, ".txt"); // string_view
|
|
108
|
+
ends_with("literal", ".txt"); // const char* converts to string_view
|
|
109
|
+
ends_with(sv, std::string(".log")); // string converts to string_view
|
|
110
|
+
|
|
111
|
+
// AVOID: storing string_view as a class member — it doesn't own the data
|
|
112
|
+
// (only safe if another owning string keeps the data alive)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## `std::variant` (C++17)
|
|
116
|
+
|
|
117
|
+
Type-safe union — an object that holds one of several specified types.
|
|
118
|
+
|
|
119
|
+
```cpp
|
|
120
|
+
#include <variant>
|
|
121
|
+
|
|
122
|
+
std::variant<int, std::string, double> v = 42;
|
|
123
|
+
|
|
124
|
+
// Check and access
|
|
125
|
+
if (std::holds_alternative<int>(v)) {
|
|
126
|
+
int i = std::get<int>(v);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Visitor pattern
|
|
130
|
+
std::visit([](const auto& val) {
|
|
131
|
+
std::cout << val << '\n';
|
|
132
|
+
}, v);
|
|
133
|
+
|
|
134
|
+
// Overload helper (C++17 doesn't have std::visit overload; use this pattern)
|
|
135
|
+
template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
|
|
136
|
+
template <class... Ts> overload(Ts...) -> overload<Ts...>;
|
|
137
|
+
|
|
138
|
+
std::visit(overload{
|
|
139
|
+
[](int i) { std::cout << "int: " << i << '\n'; },
|
|
140
|
+
[](const std::string& s) { std::cout << "string: " << s << '\n'; },
|
|
141
|
+
[](double d) { std::cout << "double: " << d << '\n'; }
|
|
142
|
+
}, v);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## `std::any` (C++17)
|
|
146
|
+
|
|
147
|
+
Holds any single value of any type. Use when the set of possible types is open-ended.
|
|
148
|
+
|
|
149
|
+
```cpp
|
|
150
|
+
#include <any>
|
|
151
|
+
|
|
152
|
+
std::any a = 42;
|
|
153
|
+
a = std::string("hello");
|
|
154
|
+
a = 3.14;
|
|
155
|
+
|
|
156
|
+
if (a.type() == typeid(double)) {
|
|
157
|
+
double d = std::any_cast<double>(a);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Safer: check before cast
|
|
161
|
+
if (auto* pd = std::any_cast<double>(&a)) {
|
|
162
|
+
std::cout << *pd << '\n';
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Ranges (C++20)
|
|
167
|
+
|
|
168
|
+
```cpp
|
|
169
|
+
#include <ranges>
|
|
170
|
+
#include <algorithm>
|
|
171
|
+
|
|
172
|
+
std::vector<int> v = {1, 2, 3, 4, 5, 6};
|
|
173
|
+
|
|
174
|
+
// Filter and transform lazily — no new vector allocation
|
|
175
|
+
auto evens = v | std::views::filter([](int x) { return x % 2 == 0; });
|
|
176
|
+
auto doubled = evens | std::views::transform([](int x) { return x * 2; });
|
|
177
|
+
|
|
178
|
+
for (int x : doubled) std::cout << x << ' '; // 4 8 12
|
|
179
|
+
|
|
180
|
+
// Range-based sort (C++20 — simpler than iterators)
|
|
181
|
+
std::ranges::sort(v);
|
|
182
|
+
|
|
183
|
+
// Find
|
|
184
|
+
auto it = std::ranges::find(v, 3);
|
|
185
|
+
|
|
186
|
+
// All / any / none
|
|
187
|
+
bool has_negative = std::ranges::any_of(v, [](int x) { return x < 0; });
|
|
188
|
+
|
|
189
|
+
// Projections — sort by member without lambda boilerplate
|
|
190
|
+
struct Employee { std::string name; int level; };
|
|
191
|
+
std::vector<Employee> emps{{"Bob", 3}, {"Alice", 1}, {"Carol", 2}};
|
|
192
|
+
std::ranges::sort(emps, {}, &Employee::level); // sort by .level
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Concepts (C++20)
|
|
196
|
+
|
|
197
|
+
Explicit, named constraints on template parameters. Replace SFINAE with readable syntax.
|
|
198
|
+
|
|
199
|
+
```cpp
|
|
200
|
+
#include <concepts>
|
|
201
|
+
#include <type_traits>
|
|
202
|
+
|
|
203
|
+
// Define a concept
|
|
204
|
+
template <typename T>
|
|
205
|
+
concept Numeric = std::integral<T> || std::floating_point<T>;
|
|
206
|
+
|
|
207
|
+
// Use in template parameters
|
|
208
|
+
template <Numeric T>
|
|
209
|
+
T add(T a, T b) { return a + b; }
|
|
210
|
+
|
|
211
|
+
static_assert(add(1, 2) == 3);
|
|
212
|
+
static_assert(add(1.5, 2.5) == 4.0);
|
|
213
|
+
// add("a", "b") — fails to instantiate: const char* is not Numeric
|
|
214
|
+
|
|
215
|
+
// Built-in concepts
|
|
216
|
+
static_assert(std::ranges::range<std::vector<int>>);
|
|
217
|
+
static_assert(std::copyable<int>);
|
|
218
|
+
|
|
219
|
+
// Combining concepts
|
|
220
|
+
template <typename T>
|
|
221
|
+
concept Comparable = requires(T a, T b) {
|
|
222
|
+
{ a < b } -> std::convertible_to<bool>;
|
|
223
|
+
{ a == b } -> std::convertible_to<bool>;
|
|
224
|
+
};
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## `std::format` (C++20)
|
|
228
|
+
|
|
229
|
+
Type-safe, Python-style string formatting.
|
|
230
|
+
|
|
231
|
+
```cpp
|
|
232
|
+
#include <format>
|
|
233
|
+
#include <chrono>
|
|
234
|
+
|
|
235
|
+
// Instead of printf/sprintf/cout combos
|
|
236
|
+
std::string s = std::format("{} + {} = {}", 1, 2, 3); // "1 + 2 = 3"
|
|
237
|
+
std::string t = std::format("{:>10}", 42); // right-aligned, width 10
|
|
238
|
+
std::string u = std::format("{:.2f}", 3.14159); // "3.14"
|
|
239
|
+
std::string v = std::format("{:02x}", 255); // "ff"
|
|
240
|
+
|
|
241
|
+
// Named arguments
|
|
242
|
+
std::cout << std::format("Hello, {name}! You are {age} years old.\n",
|
|
243
|
+
std::format_args::arg("name", "Alice"), /* arg */);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## `std::span` (C++20)
|
|
247
|
+
|
|
248
|
+
Non-owning view into a contiguous sequence — replacement for pointer+length pairs.
|
|
249
|
+
|
|
250
|
+
```cpp
|
|
251
|
+
#include <span>
|
|
252
|
+
|
|
253
|
+
// Instead of (T* ptr, size_t count)
|
|
254
|
+
void print_all(std::span<const int> data) {
|
|
255
|
+
for (int x : data) std::cout << x << ' ';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
std::vector<int> vec{1, 2, 3};
|
|
259
|
+
std::array<int, 3> arr{4, 5, 6};
|
|
260
|
+
int raw[] = {7, 8, 9};
|
|
261
|
+
|
|
262
|
+
print_all(vec); // works
|
|
263
|
+
print_all(arr); // works
|
|
264
|
+
print_all(raw); // works — decays to span<int, 3>
|
|
265
|
+
print_all({raw, 2}); // explicit length
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## `[[likely]]` / `[[unlikely]]` (C++20)
|
|
269
|
+
|
|
270
|
+
Branch prediction hints for performance-critical paths.
|
|
271
|
+
|
|
272
|
+
```cpp
|
|
273
|
+
bool process(int x) {
|
|
274
|
+
if (x > 0) [[likely]] {
|
|
275
|
+
// compiler may rearrange code for the common case
|
|
276
|
+
return do_positive(x);
|
|
277
|
+
}
|
|
278
|
+
[[unlikely]] {
|
|
279
|
+
return do_non_positive(x);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# C++ Code Review Checklist
|
|
2
|
+
|
|
3
|
+
Use this checklist when reviewing C++ pull requests or before committing.
|
|
4
|
+
|
|
5
|
+
## Memory Safety
|
|
6
|
+
|
|
7
|
+
- [ ] No raw `new` or `delete` — use `std::unique_ptr` / `std::make_unique`
|
|
8
|
+
- [ ] No owning raw pointers as class members (prefer `std::unique_ptr`)
|
|
9
|
+
- [ ] Shared ownership genuinely needed? Consider if `unique_ptr` suffices
|
|
10
|
+
- [ ] `shared_ptr` cycles broken with `weak_ptr`?
|
|
11
|
+
- [ ] Virtual destructor declared on any polymorphic base class
|
|
12
|
+
- [ ] All special member functions (Rule of Five) declared if needed
|
|
13
|
+
- [ ] No use-after-free: references/pointers outlive the data they refer to
|
|
14
|
+
- [ ] Arrays allocated with `new Type[n]` are `delete[]`d (or better, use `vector`)
|
|
15
|
+
|
|
16
|
+
## RAII and Resource Management
|
|
17
|
+
|
|
18
|
+
- [ ] File handles, mutexes, sockets use RAII wrappers (destructor cleans up)
|
|
19
|
+
- [ ] `std::lock_guard` / `std::scoped_lock` used for mutex locking — not raw `.lock()/.unlock()`
|
|
20
|
+
- [ ] Every `std::thread` has `.join()` or `.detach()` called before destruction
|
|
21
|
+
- [ ] Prefer `std::jthread` (C++20) for automatic stop and join
|
|
22
|
+
|
|
23
|
+
## Const Correctness
|
|
24
|
+
|
|
25
|
+
- [ ] Member functions that don't modify state are marked `const`
|
|
26
|
+
- [ ] Non-modifying parameters passed by `const&` (or by value if cheap-to-copy)
|
|
27
|
+
- [ ] `const` variables used where data doesn't change after init
|
|
28
|
+
- [ ] `const_iterator` used when iteration doesn't modify
|
|
29
|
+
|
|
30
|
+
## Modern C++ Usage
|
|
31
|
+
|
|
32
|
+
- [ ] Uses C++17/20 features where appropriate (structured bindings, `if constexpr`, `std::optional`, `string_view`)
|
|
33
|
+
- [ ] No C-style casts — uses `static_cast`, `reinterpret_cast`, `const_cast`
|
|
34
|
+
- [ ] `[[nodiscard]]` on functions returning values that must not be ignored
|
|
35
|
+
- [ ] `override` used on all virtual method overrides
|
|
36
|
+
- [ ] `noexcept` used correctly (only on functions that truly cannot throw)
|
|
37
|
+
- [ ] `std::make_unique` / `std::make_shared` used instead of direct `new`
|
|
38
|
+
|
|
39
|
+
## Headers and Includes
|
|
40
|
+
|
|
41
|
+
- [ ] No `using namespace std;` in header files
|
|
42
|
+
- [ ] No unused `#include` directives
|
|
43
|
+
- [ ] Include order: related header first, then standard library, then external libs
|
|
44
|
+
- [ ] Header is self-contained (includes everything it uses)
|
|
45
|
+
|
|
46
|
+
## Containers and Types
|
|
47
|
+
|
|
48
|
+
- [ ] `std::vector` preferred over raw arrays for dynamic sequences
|
|
49
|
+
- [ ] `std::vector::reserve()` called when size is known ahead of time
|
|
50
|
+
- [ ] `std::array` used for fixed-size compile-time-known arrays
|
|
51
|
+
- [ ] `std::string_view` used for read-only string params (no ownership)
|
|
52
|
+
- [ ] `std::optional` used instead of sentinel values (e.g., `-1`, `nullptr`) for optional values
|
|
53
|
+
- [ ] No `std::endl` in hot paths (flushes the stream)
|
|
54
|
+
|
|
55
|
+
## Error Handling
|
|
56
|
+
|
|
57
|
+
- [ ] Expected failures (not found, invalid input) handled without exceptions
|
|
58
|
+
- [ ] Exceptions used only for truly exceptional/unrecoverable conditions
|
|
59
|
+
- [ ] No exception thrown from destructors (use `noexcept` and `std::terminate` instead if needed)
|
|
60
|
+
- [ ] Exception safety level appropriate for the function (at minimum: no resource leaks)
|
|
61
|
+
- [ ] `std::expected<T, E>` (C++23) or error codes used in library/API code
|
|
62
|
+
|
|
63
|
+
## Concurrency
|
|
64
|
+
|
|
65
|
+
- [ ] Shared mutable state protected by `std::mutex` / `std::lock_guard`
|
|
66
|
+
- [ ] No data races on shared variables
|
|
67
|
+
- [ ] `std::atomic` used for simple shared counters and flags
|
|
68
|
+
- [ ] Thread lifecycle managed correctly (`join()`/`detach()` or `jthread`)
|
|
69
|
+
- [ ] No passing of references to locals into detached threads
|
|
70
|
+
|
|
71
|
+
## Performance Considerations
|
|
72
|
+
|
|
73
|
+
- [ ] No unnecessary copies (pass by `const&`, use `std::move`, prefer move over copy)
|
|
74
|
+
- [ ] No hidden allocations in hot paths (e.g., `s + "text"` inside a loop)
|
|
75
|
+
- [ ] Vector `reserve()` before known-size loops
|
|
76
|
+
- [ ] Emplacement used when inserting into containers: `vec.emplace_back(args...)` over `vec.push_back(Type(args...))`
|
|
77
|
+
|
|
78
|
+
## Initialization
|
|
79
|
+
|
|
80
|
+
- [ ] All variables initialized before use
|
|
81
|
+
- [ ] Member variables initialized in initializer list or with default member initializers
|
|
82
|
+
- [ ] No use of default-initialized built-in types without assignment
|
|
83
|
+
|
|
84
|
+
## Code Quality
|
|
85
|
+
|
|
86
|
+
- [ ] No `goto` statements
|
|
87
|
+
- [ ] No macros for anything that can be a `constexpr`, `inline`, or template
|
|
88
|
+
- [ ] Template code in headers uses explicit `typename T` or `class T`
|
|
89
|
+
- [ ] No hardcoded magic numbers — use named constants or `constexpr`
|
|
90
|
+
- [ ] `auto` used appropriately (not hiding intent)
|
|
91
|
+
|
|
92
|
+
## Testing
|
|
93
|
+
|
|
94
|
+
- [ ] New code has corresponding unit tests
|
|
95
|
+
- [ ] Edge cases covered: empty containers, zero/negative values, boundary conditions
|
|
96
|
+
- [ ] No commented-out test code committed
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# C++ Testing Skill
|
|
2
|
+
|
|
3
|
+
C++ testing patterns for opencode. Covers unit, integration, and host-side tests, framework selection (GoogleTest / Catch2 / doctest), mocking strategies, TDD workflow, and the 80% coverage gate.
|
|
4
|
+
|
|
5
|
+
## What it provides
|
|
6
|
+
|
|
7
|
+
- **SKILL.md** — framework selection + 5 deep-dive references
|
|
8
|
+
- **references/framework-compare.md** — GoogleTest vs Catch2 vs doctest with code samples
|
|
9
|
+
- **references/host-test-for-embedded.md** — pure-CMake host test for firmware `.cpp` without `idf.py`/FreeRTOS
|
|
10
|
+
- **references/mocking.md** — abstract interfaces, link-time seam, `std::function` injection, gmock
|
|
11
|
+
- **references/tdd-workflow.md** — red-green-refactor in C++
|
|
12
|
+
- **references/coverage.md** — gcov/lcov, 80% gate on `main/` and `components/`
|
|
13
|
+
|
|
14
|
+
## When it triggers
|
|
15
|
+
|
|
16
|
+
- Writing a new C++ unit or host test
|
|
17
|
+
- Choosing a test framework
|
|
18
|
+
- Setting up a host-side test binary that links firmware code without ESP-IDF
|
|
19
|
+
- Running a test-gate before merging
|
|
20
|
+
- Applying TDD to firmware policy/payload modules
|
|
21
|
+
|
|
22
|
+
## Manual install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cp -R SKILL.md references ~/.opencode/skills/cpp-testing/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The BizarHarness installer can also install this automatically — select the **C++ testing** component.
|