c-cpp-dev
Skill32 starsChanged 7 days agoMITView on GitHub
Use when writing, reviewing, or refactoring C/C++ code, especially on 32-bit ARM embedded targets. NOT for formatting-only changes, simple file reads, non-embedded C/C++ (desktop/server), or C#/Java despite the 'C' in the name.
In this repository9 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- debug-methodologySkill
- embedded-firmware-devSkill
- embedded-workbenchSkill
- fact-checkSkill
- hardfault-triageSkill
- keil-mdk-buildSkill
- state-machine-designSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
16 rules. About 0 are common, 13 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈13
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 17
When a header is shared between C and C++ translation units, never define variables in the header — not even with static.
- Distinctive: line 19
Place Doxygen comments in header files.
- Distinctive: line 20
Avoid inline functions in header files.
- Very specific: line 27
Use pragma or packing attributes only when they match a wire-format requirement and are documented clearly.
- Distinctive: line 28
Use double precision and 64-bit integers with caution on 32-bit targets.
- Distinctive: line 29
When a 64-bit value crosses a task, interrupt, or module boundary, make its access pattern explicit: copied, split, guarded, or protected …
- Distinctive: line 33
Treat heap objects and pointer-owned state as lifetime-sensitive resources.
- Distinctive: line 35
Avoid retaining borrowed pointers longer than the lifetime that guarantees validity.
- Very specific: line 61
Use __attribute__((section(...))) to place data and code in specific memory regions.
- Distinctive: line 77
Keep the ISR path minimal and use separate wrapper functions for task-context work.
- Distinctive: line 114
Keep the intent of the original code clear while changing structure.
- Distinctive: line 116
Prefer small, focused extractions over broad rewrites.
- Distinctive: line 118
If a block is short but semantically clear and reused in multiple places, extract it into an interface named by meaning, not implementation …
- Distinctive: line 119
If a C module has become difficult to keep readable without fighting the language, consider C++ refactoring — but ask the user before …
- Distinctive: line 120
When reshaping APIs or control flow, keep the normal, failure, and recovery paths intact.
- Distinctive: line 121
If a refactor touches ownership, lifetime, or allocation, consult memory-layout guidance.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<HARD-GATE>
This is a domain implementation skill. If you are planning, designing, or entering plan mode — load `Skill("embedded-workbench")` first to activate the workflow gates (Plan Verification Gate, Approval Gate, Closure Gate). Domain skills carry implementation guidance, not workflow enforcement.
</HARD-GATE>C/C++ Development
Language baseline is project-specific: check the project's CLAUDE.md, build system (-std= flags), or compiler configuration. All rules assume 32-bit ARM target unless otherwise noted.
Code Generation
- Respect the project's configured language standard. Don't assume a default.
- When a header is shared between C and C++ translation units, never define variables in the header — not even with
static. Useexternin the header and exactly one definition in a single.cfile. - Allow
gotowhen it simplifies cleanup or reduces complexity, but use it as a maintenance tool, not a first choice. - Place Doxygen comments in header files. Keep implementation files focused on behavior.
- Avoid inline functions in header files. Keep function definitions in source files so the interface stays small.
- In C, lightweight macros or helper wrappers are acceptable if simple, local, and clearly named.
Memory Layout
- Watch local variable bursts inside functions. Large automatic arrays, structs, or many temporaries are stack budget items.
- Design structures with both semantics and byte alignment in mind. For protocol parsing, byte streams, and wire formats, make packing and endianness explicit.
- Use
pragmaor packing attributes only when they match a wire-format requirement and are documented clearly. - Use double precision and 64-bit integers with caution on 32-bit targets. Operations on 64-bit objects create performance, compatibility, and atomicity problems unless the wider width is clearly required.
- When a 64-bit value crosses a task, interrupt, or module boundary, make its access pattern explicit: copied, split, guarded, or protected from tearing.
Heap and Pointer Ownership
- Treat heap objects and pointer-owned state as lifetime-sensitive resources.
- Make ownership, release point, and invalidation rules explicit.
- Avoid retaining borrowed pointers longer than the lifetime that guarantees validity.
- For heap-backed or pointer-rich code, document invalidation conditions so future changes don't accidentally reuse stale objects.
Embedded C Specifics
Hardware Register Access
Memory-mapped peripheral registers must be volatile to prevent the compiler from optimizing away repeated reads or writes. Use a volatile struct pointer (the CMSIS/HAL pattern) rather than bare casts.
// CORRECT: volatile pointer to peripheral struct
typedef struct {
volatile uint32_t SR; // Status @ offset 0x00
volatile uint32_t DR; // Data @ offset 0x04
volatile uint32_t BRR; // Baud @ offset 0x08
volatile uint32_t CR1; // Control @ offset 0x0C
} UART_Regs;
#define UART2 ((UART_Regs *)0x40004400)
// BAD: missing volatile — compiler may optimize away repeated reads
#define UART_SR (*(uint32_t *)0x40001000)
while (UART_SR & TX_BUSY); // May become infinite loop under optimizationLinker Section Placement
Use __attribute__((section(...))) to place data and code in specific memory regions.
// .noinit: value preserved across watchdog/system reset (not power-on reset)
uint32_t reset_reason __attribute__((section(".noinit")));
// .ramfunc: function kept in RAM for execution during flash programming
__attribute__((section(".ramfunc")))
void flash_program_word(uint32_t addr, uint32_t data) { /* ... */ }
// DMA-accessible buffer: must not be placed in CCM (tightly-coupled memory)
uint8_t dma_buffer[1024] __attribute__((section(".dma_ram")));ISR-Safe vs Non-ISR-Safe Path Separation
Keep the ISR path minimal and use separate wrapper functions for task-context work. Never call blocking APIs from an ISR.
// ISR path: minimal, no blocking, use ISR-safe RTOS primitives
static void uart_rx_isr(void) {
BaseType_t xHigherPriorityWoken = pdFALSE;
xSemaphoreGiveFromISR(xUartSem, &xHigherPriorityWoken);
portYIELD_FROM_ISR(xHigherPriorityWoken);
}
// Task path: can block, log, allocate, parse
void uart_rx_process_task(void) {
if (xSemaphoreTake(xUartSem, portMAX_DELAY) == pdTRUE) {
parse_and_dispatch(uart_rx_buf);
}
}Cortex-M Fault Handler (naked)
The HardFault handler must be __attribute__((naked)) to prevent the compiler's prologue from corrupting the stack pointer before the exception frame can be captured.
// naked: no prologue — SP is the exact exception frame. Required for fault handlers.
__attribute__((naked)) void HardFault_Handler(void) {
__asm volatile(
"tst lr, #4\n" // Check EXC_RETURN bit 2: MSP vs PSP
"ite eq\n"
"mrseq r0, msp\n" // Thread used MSP → read from MSP
"mrsne r0, psp\n" // Thread used PSP (RTOS) → read from PSP
"b HardFault_HandlerC\n"
);
}Refactoring
- Keep the intent of the original code clear while changing structure. A refactor should improve cohesion, readability, or maintainability without quietly changing behavior.
- Skip smell-only changes that don't materially improve correctness, risk, maintainability, or clarity.
- Prefer small, focused extractions over broad rewrites. Split one responsibility at a time.
- Move a helper into shared code only when reuse is real and the dependency surface stays simple.
- If a block is short but semantically clear and reused in multiple places, extract it into an interface named by meaning, not implementation detail.
- If a C module has become difficult to keep readable without fighting the language, consider C++ refactoring — but ask the user before switching languages.
- When reshaping APIs or control flow, keep the normal, failure, and recovery paths intact. Re-validate all three after the change.
- If a refactor touches ownership, lifetime, or allocation, consult memory-layout guidance.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

