2025-09-25 15:32:28 -04:00
# logos-cpp-sdk
2025-10-08 15:50:16 -04:00
## How to Build
### Using Nix (Recommended)
2025-10-23 09:54:45 -04:00
The project includes a Nix flake for reproducible builds with a modular structure:
#### Build Complete SDK (Library + Headers + Generator)
2025-10-08 15:50:16 -04:00
```bash
2025-10-23 09:54:45 -04:00
# Build everything (default)
2025-10-08 15:50:16 -04:00
nix build
2025-10-23 09:54:45 -04:00
# Or explicitly
nix build '.#logos-cpp-sdk'
nix build '.#default'
```
The result will include:
- `/bin/logos-cpp-generator` - Code generator binary
- `/lib/` - SDK libraries
- `/include/` - Headers (core/ and cpp/)
#### Build Individual Components
```bash
# Build only the generator binary (outputs to /bin)
nix build '.#logos-cpp-bin'
# Build only the SDK library (outputs to /lib)
nix build '.#logos-cpp-lib'
# Build only the headers (outputs to /include)
nix build '.#logos-cpp-include'
# Legacy alias for generator
nix build '.#cpp-generator'
```
#### Development Shell
```bash
2025-10-08 15:50:16 -04:00
# Enter development shell with all dependencies
nix develop
```
**Note:** In zsh, you need to quote the target (e.g., `'.#logos-cpp-sdk'` ) to prevent glob expansion.
If you don't have flakes enabled globally, add experimental flags:
```bash
nix build '.#logos-cpp-sdk' --extra-experimental-features 'nix-command flakes'
```
2025-10-23 09:54:45 -04:00
The compiled artifacts can be found at `result/`
#### Modular Architecture
The nix build system is organized into modular files in the `/nix` directory:
- `nix/default.nix` - Common configuration (dependencies, flags, metadata)
- `nix/bin.nix` - Generator binary compilation
- `nix/lib.nix` - SDK library compilation
- `nix/include.nix` - Header installation
2026-03-24 10:14:17 -04:00
- `nix/tests.nix` - Test suite (build + run via `nix build '.#tests'` )
#### Run Tests
```bash
# Build and run all tests (build fails if any test fails)
nix build '.#tests'
```
The test binaries are available in `result/bin/` and can be re-run with filters:
```bash
./result/bin/sdk_tests --gtest_filter= "LogosResultTest.*"
./result/bin/generator_tests --gtest_filter= "*PascalCase*"
```
2025-10-08 15:50:16 -04:00
### Manual Build
#### Building the C++ SDK
```bash
cd cpp
./compile.sh
```
#### Building the Code Generator
```bash
cd cpp-generator
./compile.sh
```
The generator binary will be available at `build/bin/logos-cpp-generator` .
2025-10-23 10:06:50 -04:00
## Usage
### Code Generator
The `logos-cpp-generator` tool generates C++ wrapper code for Logos plugins.
#### Basic Usage
```bash
# Generate wrapper for a single plugin (uses default output directory)
logos-cpp-generator /path/to/plugin.dylib
# Specify custom output directory
logos-cpp-generator /path/to/plugin.dylib --output-dir /custom/output/path
2025-10-23 10:15:06 -04:00
# Generate only the module files (no core manager or umbrella headers)
logos-cpp-generator /path/to/plugin.dylib --module-only
# Combine options
logos-cpp-generator /path/to/plugin.dylib --output-dir /custom/output --module-only
2025-10-23 10:06:50 -04:00
```
#### Generate from Metadata
```bash
# List dependencies from metadata.json
logos-cpp-generator --metadata /path/to/metadata.json
# Generate wrappers for all dependencies
logos-cpp-generator --metadata /path/to/metadata.json --module-dir /path/to/modules
# Generate with custom output directory
logos-cpp-generator --metadata /path/to/metadata.json --module-dir /path/to/modules --output-dir /custom/output
2025-10-23 10:15:06 -04:00
# Generate only module files (no core manager or umbrella headers)
logos-cpp-generator --metadata /path/to/metadata.json --module-dir /path/to/modules --module-only
2025-10-23 10:45:34 -04:00
# Generate only core manager and umbrella files (assumes module files already exist)
logos-cpp-generator --metadata /path/to/metadata.json --general-only
# Generate general files with custom output directory
logos-cpp-generator --metadata /path/to/metadata.json --general-only --output-dir /custom/output
2025-10-23 10:06:50 -04:00
```
2025-10-23 10:15:06 -04:00
#### Options
2025-10-23 10:06:50 -04:00
2025-10-23 10:15:06 -04:00
** `--output-dir /path/to/output` **
- **Default:** If not specified, generated files are placed in `logos-cpp-sdk/cpp/generated/`
- **Custom:** Specify any directory for the generated files
2025-10-23 10:06:50 -04:00
- The output directory will be created automatically if it doesn't exist
2025-10-23 10:15:06 -04:00
** `--module-only` **
- When specified, generates only the requested module's `.h` and `.cpp` files
- Skips generation of `core_manager_api.*` and umbrella headers (`logos_sdk.*` )
- Useful when you only need wrapper code for specific modules
2025-10-23 10:45:34 -04:00
** `--general-only` **
- When specified with `--metadata` , generates only the core manager and umbrella SDK files
- Assumes module wrapper files already exist in the output directory
- Generates: `core_manager_api.h` , `core_manager_api.cpp` , `logos_sdk.h` , `logos_sdk.cpp`
- The umbrella headers will include references to all modules listed in the metadata's `dependencies` array
- For each dependency (e.g., `"waku_module"` ), it will:
- Include `waku_module_api.h` in the header
- Include `waku_module_api.cpp` in the source
- Create a `WakuModule waku_module;` member in the `LogosModules` struct
- Does not require `--module-dir` since it doesn't process plugins
2025-10-23 10:15:06 -04:00
#### Generated Files
2025-10-23 10:45:34 -04:00
**By default (without `--module-only` or `--general-only`):**
2025-10-23 10:06:50 -04:00
- `<module>_api.h` and `<module>_api.cpp` - Wrapper code for each module
- `core_manager_api.h` and `core_manager_api.cpp` - Core manager wrapper
- `logos_sdk.h` and `logos_sdk.cpp` - Umbrella headers including all modules
2025-10-23 10:45:34 -04:00
**With `--module-only`:**
2025-10-23 10:15:06 -04:00
- Only `<module>_api.h` and `<module>_api.cpp` - Wrapper code for the requested module(s)
2025-10-23 10:45:34 -04:00
**With `--general-only`:**
- Only `core_manager_api.h` and `core_manager_api.cpp` - Core manager wrapper
- Only `logos_sdk.h` and `logos_sdk.cpp` - Umbrella headers that reference existing module files
#### Typical Workflow
A common workflow is to generate module wrappers separately, then generate the umbrella SDK:
```bash
# Step 1: Generate individual module wrappers
logos-cpp-generator /path/to/plugin1.dylib --module-only --output-dir ./generated
logos-cpp-generator /path/to/plugin2.dylib --module-only --output-dir ./generated
# Step 2: Generate core manager and umbrella SDK (references modules from step 1)
logos-cpp-generator --metadata metadata.json --general-only --output-dir ./generated
```
This approach gives you fine-grained control over which modules to include and allows rebuilding just the umbrella headers without regenerating all module wrappers.
2026-02-25 18:13:43 +04:00
### API
#### LogosResult
`LogosResult` provides a structured way to return either a value or an error from synchronous method calls.
If the `success` attribute is `true` , you can retrieve the value using a cast. Otherwise, retrieve the error which should be a string (though not enforced).
The `success` attribute should ALWAYS be asserted. Accessing the value of an errored `LogosResult` or the error of a valid `LogosResult` will result in a `LogosResultException` being thrown.
### Example
```cpp
LogosResult result = m_logos -> my_module . someMethod ();
if ( result . success ) {
// Use shorthand
QString value = result . getString ();
// Or
QString value = result . getValue < QString > ();
} else {
// Use shorthand
QString error = result . getError ();
// Or
QString error = result . getError < QString > ();
}
```
#### Complex objects
Let's say you need to return a complex object. In the SDK, you have to build your type with primitive like QVariantMap:
```cpp
// Received JSON: {"cid": "QmXyz...", "filename": "photo.jpg", "size": 2048576, "mimetype": "image/jpeg"}
QVariantMap manifest ;
manifest [ "cid" ] = "QmXyz..." ;
manifest [ "filename" ] = "photo.jpg" ;
manifest [ "size" ] = 2048576 ;
manifest [ "mimetype" ] = "image/jpeg" ;
return { true , manifest };
```
And then to consume by using the shorthand function:
```cpp
LogosResult result = m_logos -> my_plugin . someMethod ( cid );
if ( result . success ) {
QString cid = result . getString ( "cid" );
// You can define a default value as well
QString cid = result . getString ( "cid" , "unknown" );
}
```
Or you can use the value directly:
```cpp
LogosResult result = m_logos -> my_plugin . someMethod ( cid );
if ( result . success ) {
QVariantMap manifest = result . getMap ();
QString cid = manifest [ "cid" ]. toString ();
}
```
Same thing for a list, you can use `QVariantList` :
```cpp
QVariantList manifests ;
QVariantMap m1 ;
m1 [ "cid" ] = "QmAbc..." ;
m1 [ "filename" ] = "document.pdf" ;
m1 [ "size" ] = 1024000 ;
manifests . append ( m1 );
QVariantMap m2 ;
m2 [ "cid" ] = "QmDef..." ;
m2 [ "filename" ] = "image.png" ;
m2 [ "size" ] = 512000 ;
manifests . append ( m2 );
return { true , manifests };
```
To consume it using the shorthand function:
```cpp
LogosResult result = m_logos -> my_plugin . someMethod ();
if ( result . success ) {
for ( int i = 0 ; i < list . size (); ++ i ) {
QString cid = result . getString ( i , "cid" );
// You can define a default value as well
QString cid = result . getString ( 0 , "cid" , "unknown" );
}
}
```
Or you can use the value directly:
```cpp
LogosResult result = m_logos -> my_plugin . someMethod ();
if ( result . success ) {
QVariantList list = result . getList ();
for ( const QVariant & item : list ) {
QVariantMap manifest = item . toMap ();
QString cid = manifest [ "cid" ]. toString ();
}
}
```
2026-05-07 12:27:14 -03:00
### Consuming the SDK
The SDK installs a CMake package. Consumers use `find_package` :
```cmake
find_package ( logos-cpp-sdk REQUIRED )
target_link_libraries ( my_target PRIVATE logos-cpp-sdk::logos_sdk )
```
The package config re-resolves transitive dependencies (`Qt6 Core/RemoteObjects` , `Boost system` , `OpenSSL` , `nlohmann_json` ), so consumers don't have to wire them up manually. The static archive references OpenSSL `SSL_CTX_*` /`X509_*` and Boost `system::error_code` ; without `find_package` 's imported target the link step fails.
### Transports
The SDK supports multiple transports, selected via `LogosTransportConfig` :
| Protocol | Backend | Use case |
|----------|---------|----------|
| `LocalSocket` | Qt Remote Objects over `QLocalSocket` | In-host, module-to-module (default) |
| `Tcp` | Boost.Asio + JSON/CBOR framing | Cross-host or container-to-host |
| `TcpSsl` | Boost.Asio + OpenSSL + JSON/CBOR framing | Same as TCP, with TLS |
A `LogosTransportSet` (= `std::vector<LogosTransportConfig>` ) lets a single provider publish on multiple endpoints simultaneously (e.g. local socket for in-process clients + TCP+SSL for remote ones):
```cpp
LogosTransportConfig local ; // protocol = LocalSocket (default)
LogosTransportConfig tls ;
tls . protocol = LogosProtocol :: TcpSsl ;
tls . host = "0.0.0.0" ;
tls . port = 7443 ;
tls . caFile = "/etc/logos/ca.pem" ;
tls . certFile = "/etc/logos/server.pem" ;
tls . keyFile = "/etc/logos/server.key" ;
LogosAPI * api = new LogosAPI ( "core_service" , LogosTransportSet { local , tls }, this );
```
For processes that want to override the SDK-wide default, use `LogosTransportConfigGlobal::setDefault()` once at startup before any `LogosAPI` is constructed.
2025-10-08 15:50:16 -04:00
### Requirements
#### Build Tools
- CMake (3.x or later)
- Ninja build system
- pkg-config
#### Dependencies
- Qt6 (qtbase)
- Qt6 Remote Objects (qtremoteobjects)
2026-05-07 12:27:14 -03:00
- Boost (system)
- OpenSSL
- nlohmann_json
2025-10-08 15:50:16 -04:00
## Supported Platforms
- macOS (aarch64-darwin, x86_64-darwin)
- Linux (aarch64-linux, x86_64-linux)