Every once in a while, a developer scratches an itch so persistent it turns into something bigger. That’s exactly what CppForge is: a response to the pain of boilerplate, inconsistencies, and the lack of elegant BDD tooling in C++.
CppForge isn’t just another framework. It’s a developer-first, test-driven, and scaffold-powered engine designed to elevate the experience of writing, testing, and maintaining C++ applications.
🧱 Why I Started This
It began with a vision: building my own portable cloud, powered by a cluster of Raspberry Pis — not in a datacenter, but right at home. I wanted services I could control, scale, and take with me. Not just as a personal cloud, but as a resilient, modular system that could one day run anywhere.
To get there, I needed the right tools — tools that understood structure, automation, and developer-first thinking. Most C++ systems lacked that. They were brittle, boilerplate-heavy, and far from portable.
CppForge is my way of changing that — by building a framework that can scaffold services, generate testable modules, and orchestrate clusters with ease. It's not just about better code — it's about creating the backbone for portable, self-sovereign cloud systems.
🔍 What CppForge Solves
- 🧪 Full BDD support in C++ with scenario-based test generation
- 🛠 Template-powered scaffolding: libraries, executables, and testable interfaces
- 💡 Feature-focused development: think in terms of outcomes, not just files
🧬 The Vision
CppForge isn't just for scaffolding or testing — it's laying the groundwork for a developer operating system for C++. Imagine a platform where:
- You write your use case in plain language
- The engine scaffolds the entire architecture
- Tests, interfaces, and integrations are wired in from the start
- You focus only on what matters: business logic
But a vision is only as powerful as the tools it delivers. So here’s what’s already working — and why it’s changing the game:
🧰 Scaffolding Engine
CppForge can generate fully structured modules — libraries or executables — with just a single scenario description. You define what the system should do, and CppForge builds the folder structure, CMake config, and interface contracts.
Think:
monitor.bdd
➝monitor/src
,monitor/include
,CMakeLists.txt
,main.cpp
, test stubs — all automatically created.
./agent
├── CMakeLists.txt
├── include
│ ├── agent.hpp
│ └── agent_factory.hpp
├── src
│ ├── agent.cpp
│ └── main.cpp
└── test
├── CMakeLists.txt
├── test_1_agent_heartbeat_agent_sends_heartbeat.cpp
├── test_2_monitor_local_services_docker_is_active.cpp
├── test_2_monitor_local_services_k3s_is_inactive.cpp
├── test_3_agent_package_reporting_agent_lists_all_packages.cpp
├── test_3_agent_package_reporting_agent_lists_simplified_packages.cpp
├── test_4_remote_command_execution_execute_shell_command_on_target_node.cpp
├── test_4_remote_command_execution_handle_command_timeouts.cpp
├── test_4_remote_command_execution_return_execution_result_to_master.cpp
├── test_4_remote_command_execution_sanitize_and_validate_incoming_commands.cpp
└── test_main.cpp
🧪 Scenario-Driven Test Generation
At the heart of CppForge is a test-first mindset. Developers write features in plain English using BDD-style syntax, and CppForge translates them into structured, ready-to-run C++ test scaffolds using Google Test.
This approach bridges the gap between behavioral expectations and actual test code — enforcing structure, encouraging clarity, and ensuring nothing gets implemented without a test.
📜 Example Feature Spec
A developer defines a feature like this:
Feature: Monitor Local Services
Scenario: Docker is active
Given Docker is installed
When the agent checks for running services
Then Docker should be reported as running
Scenario: K3s is inactive
Given k3s is not running
When the agent checks for running services
Then it should report k3s as inactive
CppForge then automatically generates a test file with:
- A virtual interface class representing the scenario
- A stub implementation that causes the test to fail until implemented
- A factory method
forge_bdd_steps()
to swap in the real implementation when ready - A runnable Google Test case that exercises the scenario
#include
#include
#include
// Step Interface
class scn_2_monitor_local_services_docker_is_active {
public:
virtual void given_docker_is_installed() {
FAIL() << "[STEP NOT IMPLEMENTED] Given Docker is installed";
}
virtual void when_the_agent_checks_for_running_services() {
FAIL() << "[STEP NOT IMPLEMENTED] When the agent checks for running services";
}
virtual void then_docker_should_be_reported_as_running() {
FAIL() << "[STEP NOT IMPLEMENTED] Then Docker should be reported as running";
}
virtual ~scn_2_monitor_local_services_docker_is_active() = default;
};
// Stub fallback
class stub_scn_2_monitor_local_services_docker_is_active : public scn_2_monitor_local_services_docker_is_active {};
#ifdef HAS_IMPL_scn_2_monitor_local_services_docker_is_active
std::unique_ptr<scn_2_monitor_local_services_docker_is_active> forge_bdd_steps() {
return std::make_unique<impl_scn_2_monitor_local_services_docker_is_active>();
}
#else
std::unique_ptr<scn_2_monitor_local_services_docker_is_active> forge_bdd_steps() {
return std::make_unique<stub_scn_2_monitor_local_services_docker_is_active>();
}
#endif
// Executable scenario
TEST(scn_2_monitor_local_services_docker_is_active_test, executes_scenario) {
auto steps = forge_bdd_steps();
std::cout << "[GIVEN] Given Docker is installed" << std::endl;
steps->given_docker_is_installed();
std::cout << "[WHEN] When the agent checks for running services" << std::endl;
steps->when_the_agent_checks_for_running_services();
std::cout << "[THEN] Then Docker should be reported as running" << std::endl;
steps->then_docker_should_be_reported_as_running();
}
🧑💻 Developer Implementation
Once ready, the developer creates their real implementation:
class impl_scn_2_monitor_local_services_docker_is_active
: public scn_2_monitor_local_services_docker_is_active {
public:
void given_docker_is_installed() override {
// Simulate installation or validate system state
}
void when_the_agent_checks_for_running_services() override {
// Trigger service check logic
}
void then_docker_should_be_reported_as_running() override {
// Assert result or state output
ASSERT_TRUE(service_report().docker_running);
}
};
The test now passes — and the developer never has to touch the test runner logic. All they write is the behavior logic in one clean implementation file.
🧠 Smart CMake Integration
Every module, whether it’s an app or a library, is wired into your project’s build system automatically. CppForge knows how to cleanly register, link, and organize CMake targets — even supporting modular test discovery.
You never have to touch CMake unless you want to.
🧱 Composable Interfaces
CppForge creates standard, override-ready interfaces for every generated module. These are designed for testability, extension, and inversion of control. Developers write only the logic; the glue is already there.
Encourages a clean separation between core logic and system boundaries.
🔌 Plugin-Ready Foundation
From the start, the engine is built with plugins in mind — whether for source analysis, metadata extraction, DI, or CI hooks. CppForge is positioning itself as a pluggable, opinionated, developer-focused platform.
Future tools like DI or clustering will simply drop into place.
🧭 Run Configuration Support
Scenarios generate test runners with built-in CLion support — each test can have its own green button, making BDD development visible, trackable, and clickable.
That scenario you defined? It now lives in your IDE’s test runner — no config required.
This is just scratching the surface — we’re building towards something where the developer experience leads the architecture, not the other way around.