Files
logos-dev-boost/guidelines/universal-module.md
2026-04-02 15:13:59 -04:00

91 lines
3.1 KiB
Markdown

# Universal Module Development
## The Universal Interface Pattern
Universal modules use **pure C++** for their implementation. You write a single implementation class using standard C++ types. The build system generates all Qt/plugin infrastructure automatically via `logos-cpp-generator --from-header`.
**You write:** A C++ class with `std::string`, `int64_t`, `bool`, `std::vector<T>`.
**The generator produces:** Qt plugin class, method dispatch, introspection metadata.
## Rules
- **NO Qt types** in your impl header or implementation: no `QString`, `QObject`, `Q_INVOKABLE`, `QVariant`
- **NO Qt includes** in your impl header (Qt headers in `.cpp` are OK if needed for internal use, but the public API must be pure C++)
- Set `"interface": "universal"` in `metadata.json`
- Name the impl class `<PascalCaseName>Impl` (e.g., `CryptoUtilsImpl`)
- Name the impl header `<name>_impl.h` (e.g., `crypto_utils_impl.h`)
- Only `public` methods become module API methods. Private/protected are ignored by the generator.
- Constructors, destructors, typedefs, and using declarations are skipped by the generator.
## Type Mapping
| Use this in your C++ | Generator maps to | Qt type produced |
|----------------------|-------------------|-----------------|
| `std::string` / `const std::string&` | `tstr` | `QString` |
| `bool` | `bool` | `bool` |
| `int64_t` | `int` | `int` |
| `uint64_t` | `uint` | `int` |
| `double` | `float64` | `double` |
| `void` | `void` | `void` |
| `std::vector<std::string>` | `[tstr]` | `QStringList` |
| `std::vector<uint8_t>` | `bstr` | `QByteArray` |
| `std::vector<int64_t>` | `[int]` | `QVariantList` |
| `std::vector<double>` | `[float64]` | `QVariantList` |
| `std::vector<bool>` | `[bool]` | `QVariantList` |
If you use a type not in this table, the generator maps it to `any` (`QVariant`). Prefer explicit types from the table for type safety.
## Impl Header Template
```cpp
#pragma once
#include <string>
#include <vector>
#include <cstdint>
class MyModuleImpl {
public:
MyModuleImpl();
~MyModuleImpl();
std::string doSomething(const std::string& input);
bool validate(const std::string& data);
int64_t count();
std::vector<std::string> listItems();
private:
// Private members are not exposed as module API
};
```
## Build Pipeline
The `flake.nix` `preConfigure` hook runs the generator before CMake:
```bash
logos-cpp-generator --from-header src/<name>_impl.h \
--backend qt \
--impl-class <ImplClassName> \
--impl-header <name>_impl.h \
--metadata metadata.json \
--output-dir ./generated_code
```
This produces `generated_code/<name>_qt_glue.h` and `generated_code/<name>_dispatch.cpp`. These files are listed in `CMakeLists.txt` under the `SOURCES` of `logos_module()`.
## Testing
Unit tests instantiate the impl class directly — it is a plain C++ class:
```cpp
#include "my_module_impl.h"
// No Qt test framework needed for basic tests
MyModuleImpl impl;
assert(impl.doSomething("test") == "expected");
```
Integration tests use `logoscore`:
```bash
logoscore -m ./result/lib -l my_module -c "my_module.doSomething(test)"
```