Skip to content

Instantly share code, notes, and snippets.

@KyleWMiller
Last active November 12, 2025 17:27
Show Gist options
  • Select an option

  • Save KyleWMiller/44e4bf4fc1d8f08d3f1b69ab62274129 to your computer and use it in GitHub Desktop.

Select an option

Save KyleWMiller/44e4bf4fc1d8f08d3f1b69ab62274129 to your computer and use it in GitHub Desktop.

C++ Code as Prose: Grammar Model for Developers

Overview

This document presents a mental model for writing C++ code that reads like natural English prose, making codebases more intuitive and maintainable.

Core Concept: Code as Sentences

graph LR
    subgraph "English Sentence"
        A[Subject] --> B[Verb]
        B --> C[Object]
    end
    
    subgraph "C++ Statement"
        D[Actor/Owner] --> E[Action/Method]
        E --> F[Target/Data]
    end
    
    A -.->|maps to| D
    B -.->|maps to| E
    C -.->|maps to| F
Loading

Parts of Speech in C++

1. Nouns: Types, Variables & Objects

graph TD
    subgraph "C++ Nouns with Ownership"
        N[Noun/Variable]
        N --> S[Stack-Owned<br/>'User user']
        N --> P[Pointer<br/>'User* userPtr']
        N --> U[Unique Ownership<br/>'std::unique_ptr&lt;User&gt;']
        N --> SH[Shared Ownership<br/>'std::shared_ptr&lt;User&gt;']
        N --> R[Reference<br/>'const User& userRef']
        
        S --> |"Reads as"| S1["'a user'"]
        P --> |"Reads as"| P1["'reference to user'"]
        U --> |"Reads as"| U1["'the owned user'"]
        SH --> |"Reads as"| SH1["'our shared user'"]
        R --> |"Reads as"| R1["'the observed user'"]
    end
Loading

2. Adjectives: Qualifiers & Constness

graph LR
    subgraph "CV-Qualifiers as Adjectives"
        Q[Qualifier]
        Q --> CONST[const<br/>'unchangeable']
        Q --> VOL[volatile<br/>'unpredictable']
        Q --> MUT[mutable<br/>'changeable']
        Q --> CONSTEXPR[constexpr<br/>'compile-time known']
    end
Loading

3. Verbs: Methods with Qualifiers

graph TD
    subgraph "Method Moods"
        M[Method]
        M --> I[Interrogative<br/>'Query State']
        M --> IM[Imperative<br/>'Change State']
        M --> S[Subjunctive<br/>'Try/Maybe']
        
        I --> IC["bool isEmpty() const<br/>'Is it empty?'"]
        IM --> IMC["void clear()<br/>'Clear it!'"]
        S --> SC["bool tryParse()<br/>'Try to parse'"]
    end
Loading

Ownership Grammar Pattern

flowchart TB
    subgraph "Ownership Clarity Rule"
        START[Variable Declaration]
        START --> Q1{Who owns?}
        
        Q1 -->|Exclusive| U[unique_ptr]
        Q1 -->|Shared| S[shared_ptr]
        Q1 -->|Nobody| R[Raw ptr/ref]
        Q1 -->|Weak ref| W[weak_ptr]
        
        U --> U1["'I own this'"]
        S --> S1["'We share this'"]
        R --> R1["'I observe this'"]
        W --> W1["'I might access this'"]
    end
Loading

RAII as Subject-Verb-Object-Lifetime

sequenceDiagram
    participant Scope
    participant Guard
    participant Mutex
    participant Resource
    
    Scope->>Guard: Create lock_guard
    Guard->>Mutex: Lock
    Note over Guard,Mutex: "Guard locks mutex"
    
    Scope->>Resource: Process data
    Note over Resource: "Process shared data"
    
    Scope->>Guard: Scope ends
    Guard->>Mutex: Unlock (destructor)
    Note over Guard,Mutex: "Guard unlocks mutex"
Loading

Template Grammar: Generic Sentences

graph LR
    subgraph "Template Pattern"
        T["template&lt;typename T&gt;"]
        T --> F["T findMaximum(vector&lt;T&gt;&)"]
        F --> I1["findMaximum&lt;int&gt;"]
        F --> I2["findMaximum&lt;string&gt;"]
        
        F -.-> R1["'For any type T,<br/>find maximum'"]
        I1 -.-> R2["'Find maximum<br/>of integers'"]
        I2 -.-> R3["'Find maximum<br/>of strings'"]
    end
Loading

Move Semantics: Ownership Transfer

stateDiagram-v2
    [*] --> OwnerA: unique_ptr created
    OwnerA --> Moving: move called
    Moving --> OwnerB: Ownership transferred
    OwnerA --> Invalid: After move
    
    note right of Moving: Transfer verb
    note right of Invalid: Source invalidated
Loading

Reading Code as Prose: Complete Example

flowchart TD
    subgraph "Code Structure"
        A["std::lock_guard&lt;std::mutex&gt; lock(dataMutex_)"]
        A --> B["if (auto user = findUserById(userId))"]
        B --> C["const auto& profile = user->getProfile()"]
        C --> D["if (!profile.isExpired())"]
        D --> E["messageQueue_.emplace(...)"]
    end
    
    subgraph "English Translation"
        A1["Lock the mutex for this scope"]
        A1 --> B1["If finding user yields a value"]
        B1 --> C1["Get the user's profile by reference"]
        C1 --> D1["If profile is not expired"]
        D1 --> E1["Construct message in queue"]
    end
    
    A -.->|reads as| A1
    B -.->|reads as| B1
    C -.->|reads as| C1
    D -.->|reads as| D1
    E -.->|reads as| E1
Loading

Type Grammar Quick Reference

graph TD
    subgraph "Type System Grammar"
        TG[Type Grammar]
        TG --> ART[Articles<br/>Storage Class]
        TG --> POS[Possessives<br/>Ownership]
        TG --> DEM[Demonstratives<br/>Pointers/Refs]
        TG --> ADJ[Adjectives<br/>CV-qualifiers]
        TG --> MOOD[Verb Mood<br/>Method qualifiers]
        
        ART --> ART1["User user<br/>'a user'"]
        ART --> ART2["static User user<br/>'the singleton user'"]
        
        POS --> POS1["unique_ptr&lt;T&gt;<br/>'my T'"]
        POS --> POS2["shared_ptr&lt;T&gt;<br/>'our T'"]
        
        DEM --> DEM1["User* that<br/>'that user'"]
        DEM --> DEM2["User& this<br/>'this user'"]
    end
Loading

Grammar Rules Summary

%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#e8f4f8', 'primaryTextColor':'#1a1a1a', 'primaryBorderColor':'#2563eb', 'lineColor':'#2563eb', 'secondaryColor':'#dbeafe', 'tertiaryColor':'#fef3c7', 'background':'#ffffff', 'mainBkg':'#e8f4f8', 'secondBkg':'#dbeafe', 'tertiaryBkg':'#fef3c7'}}}%%
mindmap
  root((C++ Grammar Rules))
    Ownership Clarity
      Every pointer shows ownership
      Use smart pointers
      Document raw pointer lifetime
    Const-Correctness
      Const methods don't mutate
      Const refs for read-only
      Propagate const properly
    Method Naming
      Queries use const
      Mutators are non-const
      Try/Maybe for fallible ops
    RAII Patterns
      Resources have owners
      Automatic cleanup
      Scope-based lifetime
Loading

Function Naming Patterns

graph LR
    subgraph "Query Functions"
        Q1[bool isEmpty]
        Q2[size_t getCount]
        Q3[bool hasItem]
        Q4[T* findItem]
    end
    
    subgraph "Command Functions"
        C1[void clear]
        C2[void addItem]
        C3[void removeItem]
        C4[void processData]
    end
    
    subgraph "Factory Functions"
        F1[T createItem]
        F2[unique_ptr&lt;T&gt; makeItem]
        F3[shared_ptr&lt;T&gt; buildItem]
    end
Loading

Modern C++ Patterns

graph TD
    subgraph "C++17/20 Patterns"
        SB[Structured Binding]
        SB --> SB1["auto [success, value] = tryParse()"]
        SB1 --> SB2["'Parse yields success and value'"]
        
        RF[Range-For]
        RF --> RF1["for (const auto& item : container)"]
        RF1 --> RF2["'For each item in container'"]
        
        CON[Concepts]
        CON --> CON1["requires std::integral&lt;T&gt;"]
        CON1 --> CON2["'T must be integral'"]
    end
Loading

The Litmus Test

flowchart LR
    subgraph "Good Code"
        G1["std::unique_ptr&lt;Logger&gt; logger =<br/>std::make_unique&lt;Logger&gt;(config)"]
        G1 --> G2["✅ 'Create and own a<br/>unique logger with config'"]
    end
    
    subgraph "Bad Code"
        B1["void func(SomeClass* ptr)"]
        B1 --> B2["❌ 'Function takes...<br/>pointer?' (unclear)"]
    end
Loading

Error Handling as Conditional Sentences

stateDiagram-v2
    [*] --> Try: Attempt operation
    Try --> Success: Operation succeeds
    Try --> Failure: Operation fails
    
    Success --> ReturnValue: Return result
    Failure --> ReturnError: Return error or optional
    
    note right of Try: tryParse, tryConnect
    note right of Success: Result type
    note right of Failure: optional or ErrorCode
Loading

Memory Management Narrative

sequenceDiagram
    participant Stack
    participant Heap
    participant SmartPtr
    participant Object
    
    Stack->>SmartPtr: Create smart pointer
    SmartPtr->>Heap: Allocate memory
    Heap->>Object: Construct object
    Note over Object: Object lifetime
    Stack->>SmartPtr: Scope ends
    SmartPtr->>Object: Destructor called
    SmartPtr->>Heap: Deallocate memory
    Note over Heap: Memory freed automatically
Loading

Best Practices Checklist

Grammar Element C++ Implementation Example Reads As
Clear Subject Named objects userManager.save() "User manager saves"
Active Voice Direct calls file.write(data) "File writes data"
Ownership Articles Smart pointers auto ptr = std::make_unique<T>() "Create the owned T"
Tense Consistency Method naming getData() not gotData() Present tense actions
Adjective Agreement Const propagation const T& getRef() const Const throughout
Complete Sentences Full statements if (user.isValid()) { process(); } "If user is valid, process"

Conclusion

By treating C++ code as prose with grammar rules, we can write more readable and maintainable code. The key principles are:

  1. Ownership is possession - Make it clear who owns what
  2. Const is immutability - Use const like adjectives to describe state
  3. Methods are verbs - Name them with clear actions
  4. Types are nouns - Make them concrete and meaningful
  5. RAII tells a story - Constructor begins, destructor ends
  6. Templates are patterns - Generic sentences with fill-in-the-blanks

Remember: If you can't read your code aloud as a coherent sentence, it needs refactoring.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment