Skip to content

Config

The Config module provides JSON-backed configuration with CLI argument parsing, dot-notation path access, type-safe getters, and validation. It integrates with the App framework to provide per-service config sections automatically.

Overview

The configuration workflow follows a define → load → access pattern:

  1. Define expected options with types, descriptions, and defaults
  2. Load from JSON files and/or CLI arguments (CLI overrides file)
  3. Access values with type-safe getters and dot-notation paths
cpp
#include <xtils/config/config.h>
using namespace xtils;

Config config;

// 1. Define options
config.Define("server.host", "Bind address", std::string("0.0.0.0"));
config.Define("server.port", "Listen port", int64_t(8080));
config.Define<bool>("server.ssl", "Enable TLS", false);
config.Define<std::string>("database.url", "DB connection string", "", true);  // required

// 2. Load
config.ParseArgs(argc, argv);  // handles --config-file automatically

// 3. Access
auto host = config.GetString("server.host").value_or("0.0.0.0");
auto port = config.GetInt("server.port").value_or(8080);
auto ssl = config.GetBool("server.ssl").value_or(false);
cpp
#include "xtils/config/config.h"

Define Options

cpp
// Generic define (type inferred from default value)
Config& Define(const std::string& name, const std::string& description,
               const Json& default_value, bool required = false);

// Type-explicit define
template <typename T>
Config& Define(const std::string& name, const std::string& description,
               T default_value, bool required = false);

Supported types: std::string, int64_t, double, bool, vector<int64_t>, vector<double>, vector<string>, vector<int>, vector<float>, Json.

Loading

cpp
// From CLI arguments
bool ParseArgs(int argc, const char** argv, bool allow_exit = false);
bool ParseArgs(const std::vector<std::string>& args, bool allow_exit = false);

// From JSON file
bool LoadFile(const std::string& filename);

// From JSON object or string
bool ParseJson(const Json& json);
bool Parse(const std::string& json_content);

TIP

ParseArgs automatically handles --config-file <path> — it loads the JSON file first, then applies CLI arguments as overrides.

CLI Argument Format

bash
# Flat keys
./app --server.port 9090 --server.ssl true

# With config file (file loaded first, CLI overrides)
./app --config-file prod.json --server.port 9090

# Array values
./app --allowed-origins '["http://localhost:3000","http://example.com"]'

Access (Dot-Notation Paths)

cpp
// Type-safe getters (return std::optional)
std::optional<std::string> GetString(const std::string& path) const;
std::optional<int64_t> GetInt(const std::string& path) const;
std::optional<double> GetDouble(const std::string& path) const;
std::optional<bool> GetBool(const std::string& path) const;
std::optional<Json> Get(const std::string& path) const;

// Generic template
template <typename T>
std::optional<T> Get(const std::string& path) const;

// Check existence
bool Has(const std::string& path) const;

Dot notation traverses nested JSON objects:

cpp
// Given: {"server": {"tls": {"cert": "/path/to/cert.pem"}}}
auto cert = config.GetString("server.tls.cert");  // → "/path/to/cert.pem"

Mutation

cpp
void Set(const std::string& path, const Json& value);

template <typename T>
void Set(const std::string& path, const T& value);

Validation & Help

cpp
// Check all required options are present
bool Validate() const;

// Get list of missing required fields
std::vector<std::string> MissingRequired() const;

// Generate help text (all defined options with descriptions and defaults)
std::string Help() const;

Serialization

cpp
std::string ToString() const;           // Pretty-print current config
Json ToJson() const;                    // Export as Json object
bool Save(const std::string& filename) const;  // Write to file
void Print() const;                     // Print to stdout

Integration with App Framework

When using the App framework, each service automatically receives its own config section. The section key matches the service name:

json
{
  "api": {
    "port": 8080,
    "cors_origin": "*"
  },
  "database": {
    "host": "localhost",
    "port": 5432,
    "name": "myapp"
  }
}
cpp
class ApiService : public Service<ApiService> {
 public:
  ApiService() : Service("api") {}

  void Init() override {
    // `config` is pre-populated with the "api" section
    auto port = config.GetInt("port").value_or(8080);
    auto cors = config.GetString("cors_origin").value_or("*");
  }
};

class DatabaseService : public Service<DatabaseService> {
 public:
  DatabaseService() : Service("database") {}

  void Init() override {
    // `config` is pre-populated with the "database" section
    auto host = config.GetString("host").value_or("localhost");
    auto port = config.GetInt("port").value_or(5432);
  }
};

Complete Example

cpp
#include <xtils/config/config.h>
#include <xtils/logging/logger.h>

using namespace xtils;

int main(int argc, char** argv) {
  Config config;
  
  // Define all options with descriptions
  config.Define("server.host", "Server bind address", std::string("0.0.0.0"));
  config.Define("server.port", "Server port", int64_t(8080));
  config.Define<bool>("server.ssl", "Enable SSL/TLS", false);
  config.Define<std::string>("server.cert", "TLS certificate path", "");
  config.Define<std::string>("database.url", "Database connection URL", "", true);
  config.Define("workers", "Number of worker threads", int64_t(4));

  // Parse (file + CLI)
  config.ParseArgs(argc, const_cast<const char**>(argv));

  // Validate required fields
  if (!config.Validate()) {
    auto missing = config.MissingRequired();
    for (auto& m : missing) {
      LogE("Missing required config: %s", m.c_str());
    }
    LogI("\n%s", config.Help().c_str());
    return 1;
  }

  // Use config values
  auto host = config.GetString("server.host").value_or("0.0.0.0");
  auto port = config.GetInt("server.port").value_or(8080);
  LogI("Starting server on %s:%lld", host.c_str(), port);

  // Runtime mutation
  config.Set("server.started_at", Json(time(nullptr)));
  
  // Save current config state
  config.Save("runtime_config.json");
  
  return 0;
}

Released under the MIT License.