Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Project C++ style (Allman, 4 spaces, 120 columns)
# Compatible with clang-format 18
BasedOnStyle: Microsoft
Language: Cpp
Standard: c++20

# Indent
IndentWidth: 4
TabWidth: 4
UseTab: Never

# Line length
ColumnLimit: 120

# Braces: Allman everywhere except lambdas (attached)
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true

# Pointers/references attached to type (int* p, Foo& f)
PointerAlignment: Left
ReferenceAlignment: Left
DerivePointerAlignment: false

# Access modifiers outdented from members (public: at class level)
AccessModifierOffset: -4

# Includes
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^"'
Priority: 1
- Regex: '^<boost/'
Priority: 2
- Regex: '^<'
Priority: 3
34 changes: 34 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Checks:
-*,
bugprone-*,
-bugprone-easily-swappable-parameters,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-avoid-non-const-global-variables,
modernize-*,
-modernize-use-trailing-return-type,
-modernize-concat-nested-namespaces,
performance-*,
readability-identifier-naming,
readability-redundant-*,
readability-simplify-*,
readability-container-size-empty,
readability-make-member-function-const,

WarningsAsErrors: ''

CheckOptions:
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.FunctionCase: lower_case
readability-identifier-naming.VariableCase: lower_case
readability-identifier-naming.MemberCase: lower_case
readability-identifier-naming.PrivateMemberSuffix: '_'
readability-identifier-naming.ConstantCase: lower_case
readability-identifier-naming.EnumConstantCase: CamelCase
readability-identifier-naming.NamespaceCase: lower_case

HeaderFilterRegex: '.*'
7 changes: 7 additions & 0 deletions .clangd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CompileFlags:
# Default matches `cmake --preset gcc`; use gcc/clang if you configure with the clang preset.
CompilationDatabase: build/gcc
# CompilationDatabase: build/clang

Index:
Background: Skip
228 changes: 228 additions & 0 deletions .cursor/rules/learning-module-implementation.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
---
description: Methodology for implementing new learning modules with Socratic Q/A/R patterns
alwaysApply: false
---

# Learning Module Implementation

## When to Apply

Use this methodology when implementing new learning modules (e.g., `learning_*` directories) that follow the repository's Socratic teaching pattern.

**Project docs:** Keep the [README.md](../../README.md) module table and [docs/LEARNING_PATH.md](../../docs/LEARNING_PATH.md) aligned with what is registered in each module’s `CMakeLists.txt`.

## Implementation Strategy

### Approach: One-File-at-a-Time with Immediate Validation

Implement test files sequentially with build-test-verify after each file:

1. **Implement one test file** completely
2. **Uncomment its CMakeLists.txt entry**
3. **Build the specific test target**
4. **Run the test and verify it passes**
5. **Fix any issues immediately** while context is fresh
6. **Move to next file**

**Rationale**: Immediate feedback loop catches errors in context, reduces cognitive load, and provides incremental progress validation.

## Test File Structure

Each test file should include:

### Required Elements

- **File header** with time estimate, difficulty, C++ standard
- **Test fixture** with `SetUp()` that clears `EventLog`
- **3-6 TEST_F blocks** with progressive difficulty
- **Socratic Q/A/R pattern** embedded at key learning points
- **TODO markers** for learner fill-in exercises
- **EventLog integration** for observable behavior
- **Mix of demonstrations and exercises**

### Test Independence (Critical)

- **Each test file** must be runnable in isolation
- **Each TEST_F** must be independent of other tests
- **SetUp()** must reset all shared state (EventLog, counters, etc.)
- **No test** should depend on execution order
- **No test** should leave side effects for other tests

This ensures tests can run in any order and be debugged individually.

### Progressive Difficulty

Label test scenarios:
- Easy — Foundational concepts, basic syntax
- Moderate — Practical patterns, common pitfalls
- Hard — Edge cases, performance, cleanup and cross-boundary hazards

### Q/A/R Pattern Requirements

```cpp
// Q: [Question probing mechanism understanding]
// A:
// R:
```

- Questions should probe **mechanisms**, not facts
- Focus on **observable signals** (e.g. EventLog, counters, traces—whatever the module instruments)
- Challenge assumptions about **contracts, lifetimes, aliasing**, and **structural consistency**
- Require **falsifiable reasoning**

**CRITICAL - Q/A/R Placement**:
- Q/A/R blocks **MUST be inside TEST_F blocks**, but **CAN BE** outside in helper functions where applicable
- Place questions **immediately after** the code being questioned
- Questions should reference **observable test behavior**, not abstract helper functions
- **Correct**: Q/A/R after helper function definition, before TEST_F where applicable
- **Correct**: Q/A/R inside TEST_F, after calling the helper and observing results

## Instrumentation Integration

Use existing instrumentation types:
- `Tracked` — Logs all lifecycle events
- `MoveTracked` — Tracks move operations
- `Resource` — Move-only resource type
- `EventLog::instance()` — Observable behavior verification

**Pattern**:
```cpp
EventLog::instance().clear();
// ... code under test ...
EXPECT_EQ(EventLog::instance().count_events("::ctor"), expected_count);
```

## Build Integration

### CMakeLists.txt Pattern

Uncomment entries one at a time:
```cmake
add_learning_test(test_feature tests/test_feature.cpp instrumentation)
```

### Build Command Pattern

```bash
cmake --build --preset gcc --target test_feature && \
./build/learning_module/test_feature --gtest_brief=1
```

**Note**: If CMake complains about missing presets, run `cmake --preset gcc` first to reconfigure.

## Common Pitfalls to Avoid

### File Bloat (CRITICAL)
- **Target**: 200-400 lines per file (max 450)
- **Symptom**: Excessive single-use helpers outside TEST_F blocks
- **Fix**: Prefer inline code in TEST_F; extract only for clarity or reuse
- **Balance**: Helpers are valuable when they serve the learning objective

### Q/A/R Placement (CRITICAL)
- **Prefer**: Inside TEST_F, immediately after observable behavior
- **Allow**: After helper functions where applicable
- **Principle**: Place questions immediately after the code being questioned

### Event Log Format
- **Wrong**: `"Tracked::ctor"`
- **Correct**: `"::ctor"` (matches actual instrumentation.cpp format)
- **Tip**: When searching for events, use specific substrings to avoid false matches
- **Example**: Use `"move_assign id="` instead of `"move_assign"` if your log contains both `"move_assign"` and `"before move_assign"`

### Allocator Requirements
- Must include `rebind` template for C++11/14 compatibility
- Must implement proper equality operators
- Pool allocators with `std::vector`: vector may request n>1 allocations during growth

### C++ Standard Features
- Default project language is **C++20**; use standard-restriction overrides from the main Socratic rules in chat only when intentionally narrowing a lesson.
- When a lesson targets an older feature set, verify the snippet compiles under the active standard (for example `requires` expressions need C++20).
- Example: Designated aggregate initializers are C++20; without C++20, use a constructor or member-by-member initialization instead.

### File I/O in Tests
- Use `TearDown()` to clean up test files: `std::remove("test_file.txt")`
- Ensure file operations don't interfere between tests

## Target Metrics per File

- **Lines**: 200-400 lines per test file (STRICT - do not exceed 450 lines)
- **Tests**: 3-6 TEST_F blocks
- **Questions**: 10-20 Q/A/R pairs
- **TODOs**: 2-5 fill-in exercises
- **Time estimate**: 2-4 hours per file (for learner)
- **Actual range observed**: 192-415 lines across implemented modules

**Key Principle**: TEST_F blocks are the primary teaching vehicle. Extract helpers only when they clarify or enable reuse.

## Validation Checklist

After implementing each file:

- [ ] File compiles without errors
- [ ] All tests pass (100%)
- [ ] No linter errors
- [ ] **Q/A/R pattern present at key points (prefer inside TEST_F)**
- [ ] **File is 200-450 lines (not bloated with excessive helpers)**
- [ ] Instrumentation used for observable behavior (EventLog or equivalent in that module)
- [ ] Progressive difficulty (Easy → Hard)
- [ ] TODO markers for learner exercises
- [ ] **Helper functions are minimal and necessary**

## Example Implementation Order

For an 8-file module (actual learning_modern_cpp implementation):
1. **Implement file 1** (foundational concept, e.g., lambdas)
2. Uncomment CMakeLists.txt entry
3. Build + test → fix any issues immediately
4. **Implement file 2** (next logical topic, e.g., auto)
5. Uncomment CMakeLists.txt entry
6. Build + test → verify pass
7. **Repeat for remaining files** (3-8)
8. Group by logical progression: C++ standard, then complexity
9. Run all tests together with ctest
10. Update `README.md` (module table) and `docs/LEARNING_PATH.md` if the curriculum or test counts changed

**Actual order used**: foundational topics first (lambdas, auto, uniform init, delegating ctors), then later topics (structured bindings, optional/variant/any, string_view, if constexpr)

**Important**: Files are logically ordered for learning progression, but each test file and test case must be **fully independent** — no test should depend on state from another test.

## Anti-Patterns

**Don't**:
- Implement all files before building any
- Skip test execution after building
- Continue to next file with failing tests
- Batch-fix errors across multiple files
- Ignore compilation warnings
- Create test dependencies between files
- Create single-use helpers that obscure test logic
- Exceed 450 lines per file

**Do**:
- Validate each file immediately
- Fix issues in context while fresh
- Ensure each test is fully independent
- Target 200-400 lines per file
- Extract helpers only for clarity or reuse
- Keep Q/A/R close to observable behavior

## Success Metrics

Across modules implemented:
- **learning_memory, learning_modern_cpp, learning_raii**: 16 files, 86 tests, ~4,500 lines, 98% first-time pass rate
- **learning_stl**: 5 files, 47 tests, 1,525 lines (305 lines/file avg) ✓ Within target
- **learning_error_handling**: 5 files, 60 tests, 4,049 lines (810 lines/file avg) ✗ Needs refactoring

## Design Heuristics

### Extraction Principle
Extract helpers when they **clarify** or enable **reuse**. Keep inline when extraction adds indirection without benefit.

### File Size Discipline
**Target**: 200-400 lines per file

**If exceeding 400 lines, ask**:
- Are helpers single-use?
- Can lambdas replace functions?
- Are you covering too many concepts?
- Should this split into two files?
Loading
Loading