programming-cpp-policy-based-di
Skill5 starsChanged 16 days agoMITView on GitHub
Use when injecting dependencies in C++ at compile time — hiding system calls, third-party APIs, or multiple dependencies behind swappable policy types; use when writing testable C++ without virtual dispatch overhead
In this repository50 other files it gives its agents.
- analyze-pr-commentsSkill
- architecture-analyzeSkill
- architecture-tradeoffSkill
- askSkill
- code-smellsSkill
- debugging-rocprof-sysSkill
- exploration-explore-codeSkill
- git-commitSkill
- git-gh-clientSkill
- git-prepare-pull-requestSkill
- git-pull-request-statusSkill
- gitSkill
- grill-meSkill
- library-amd-smiSkill
- notify-meSkill
- planning-architectureSkill
- planning-baseSkill
- planning-bugfixSkill
- planning-docsSkill
- planning-featureSkill
- planning-refactorSkill
- programming-cmake-best-practicesSkill
- programming-cpp-constexprSkill
- programming-cpp-design-patternsSkill
- programming-cpp-naming-rulesSkill
- programming-cppSkill
- programming-cpp-stl-algorithmsSkill
- programming-pythonSkill
- pr-review-interactiveSkill
- pr-reviewSkill
- radisha-create-skillSkill
- radisha-helpSkill
- radisha-improve-skillSkill
- radisha-skillsSkill
- radisha-updateSkill
- refactoring-techniquesSkill
- review-architectureSkill
- rocprofsys-buildSkill
- rocprofsys-configureSkill
- rocprofsysSkill
- static-analysisSkill
- testing-gtest-gmockSkill
- testing-pytestSkill
- testing-testplanSkill
- therock-build-to-commitSkill
- therock-commit-in-buildSkill
- therock-first-build-with-commitSkill
- update-pr-descriptionSkill
- verify-pmc-metricsSkill
- watchSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 11
Never store dependency references or accept deps via constructor — that mixes two different DI styles and creates dangling-reference risk.
- Distinctive: line 56
Use the 2-class + global approach: a thin wrapper struct delegates to a global mock pointer.
- Very specific: line 140
Never write hand-rolled spy structs: Every test for policy-based DI code must use MOCK_METHOD.
- Very specific: line 151
Red flag: If you find yourself writing struct spy_X { std::vector<...> captured.
≈ “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
C++ Policy-Based Dependency Injection
Compile-time DI via template policy parameters. Zero runtime overhead, full mockability, no virtual functions.
Ownership rule: The class always owns its dependencies as value members constructed from the policy type. Never store dependency references or accept deps via constructor — that mixes two different DI styles and creates dangling-reference risk.
Option 1 — Single Dependency (value member)
struct real_dependency {
return_type operation(args...) { return ::real_call(args...); }
};
template <typename Dependency = real_dependency>
struct my_class {
result_type do_work(args...) {
return dep_.operation(args...);
}
private:
Dependency dep_{};
};Option 2 — Policy Bundle (multiple dependencies)
Group all dependency type aliases into one policy struct. The outer class owns each dep as a value member:
struct real_dep_a { return_type op_a(args...); };
struct real_dep_b { return_type op_b(args...); };
struct real_policy {
using DepA = real_dep_a;
using DepB = real_dep_b;
};
template <typename Policy = real_policy>
struct my_class {
void method_a(args...) { dep_a_.op_a(args...); }
void method_b(args...) { dep_b_.op_b(args...); }
private:
typename Policy::DepA dep_a_{};
typename Policy::DepB dep_b_{};
};GMock Integration
GMock mock objects are non-copyable, so they cannot be owned by value inside the template class. Use the 2-class + global approach: a thin wrapper struct delegates to a global mock pointer.
struct gmock_dependency {
MOCK_METHOD(return_type, operation, (args...));
};
std::unique_ptr<gmock_dependency> g_mock;
struct mock_dependency {
return_type operation(args...) { return g_mock->operation(args...); }
};
template <typename Dependency = real_dependency>
struct my_class {
result_type do_work(args...) { return dep_.operation(args...); }
private:
Dependency dep_{};
};
class my_class_test : public ::testing::Test {
protected:
void SetUp() override { g_mock = std::make_unique<gmock_dependency>(); }
void TearDown() override { g_mock.reset(); }
};
using sut = my_class<mock_dependency>;
TEST_F(my_class_test, does_work) {
EXPECT_CALL(*g_mock, operation(expected_args))
.WillOnce(testing::Return(expected_value));
sut obj;
EXPECT_EQ(obj.do_work(args...), expected_value);
}Policy bundle variant — one global per dep, one wrapper per dep:
struct gmock_dep_a { MOCK_METHOD(return_type, op_a, (args...)); };
struct gmock_dep_b { MOCK_METHOD(return_type, op_b, (args...)); };
std::unique_ptr<gmock_dep_a> g_mock_a;
std::unique_ptr<gmock_dep_b> g_mock_b;
struct mock_dep_a { return_type op_a(args...) { return g_mock_a->op_a(args...); } };
struct mock_dep_b { return_type op_b(args...) { return g_mock_b->op_b(args...); } };
struct mock_policy {
using DepA = mock_dep_a;
using DepB = mock_dep_b;
};
class my_class_test : public ::testing::Test {
protected:
void SetUp() override {
g_mock_a = std::make_unique<gmock_dep_a>();
g_mock_b = std::make_unique<gmock_dep_b>();
}
void TearDown() override { g_mock_a.reset(); g_mock_b.reset(); }
};
using sut = my_class<mock_policy>;
TEST_F(my_class_test, method_a_calls_op_a) {
EXPECT_CALL(*g_mock_a, op_a(expected_args))
.WillOnce(testing::Return(expected_value));
sut obj;
obj.method_a(args...);
}When to Use Each
| Scenario | Pattern |
| One external call to hide | Option 1 — single template param |
| 2+ related dependencies | Option 2 — policy bundle |
| GMock for any policy dep | 2-class + global (mocks are non-copyable) |
| Runtime polymorphism needed | Virtual interface instead |
| Hot path, zero overhead required | Either option (no vtable) |
MANDATORY: Always Use GMock for Tests
Never write hand-rolled spy structs. Every test for policy-based DI code must use MOCK_METHOD.
MOCK_METHOD satisfies any policy concept through duck-typing — no virtual base class required.
Why GMock, not hand-rolled spies:
EXPECT_CALLverifies exact arguments — spies require manualEXPECT_EQper fieldWillOnce(Return(...))controls return values — spies hardcode them- Argument matchers (
HasSubstr,_,Eq,AllOf) handle complex inputs - Failure messages name the expectation — spy failures say "expected X got Y" with no context
InSequenceverifies call order — spies cannot
Red flag: If you find yourself writing struct spy_X { std::vector<...> captured; ... } — stop. Use MOCK_METHOD instead.
Common Mistakes
| Mistake | Fix |
Storing dep as Dep& dep_ member | Dangling reference risk — store as value Dep dep_{} |
Accepting dep in constructor (my_class(Dep& d)) | Mixes constructor injection with template DI — pick one style |
Passing dep via shared_ptr/unique_ptr ctor arg | Same mixing problem — let the class construct its own dep |
| Hand-rolled spy structs in tests | Always use MOCK_METHOD |
| Wrapper calls itself recursively | Qualify the real call explicitly (e.g. ::free_function()) |
auto parameter in C++17 | Use std::string_view or explicit template<typename T> |
Missing typename on nested alias | typename Policy::DepA, not Policy::DepA |
Calling g_mock.op() on unique_ptr | Dereference first: g_mock->op() |
Forgetting override on SetUp/TearDown | Always add override; base declares them virtual |
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.

