Powerful, Customizable, Header-only C++ library for configurations in JSON, YAML and env vars with configuration listeners and thread safety functionality.
Config manager simplifies C++ configuration with high performance and ease of use. Unlike Python's many tools and libs, C++ lacks versatile APIs outside the STL for configuration, and Boost doesn't fully address complex configurations or JSON/YAML interoperable handling. So often, our projects grow from simple to complex with hardcoded settings.
Config manager enables easy setting, loading, saving, and modifying of configurations, making it ideal for sharing projects. It provides a powerful, flexible, and performant solution for diverse configuration needs, ensuring quick startup and user-friendly operation.
#include "configuration.hpp"
using namespace config;
// Create a default config instance
Config &config = Config::instance();
// Create a key called 'name' with a value 'example'
config.set("name", "example");
// Retrieve the value of the key 'name'
std::cout << config.get("name") << std::endl;
// Expected result:
example
-
Unified Configuration Handling: Handles JSON, YAML, and environment variables in one library.
- Includes the ability to convert configs between JSON, YAML, and ENV with simple API calls, and output files in any of those formats.
- (Hint: See format_manager.hpp in the include/ directory)
-
Header-Only Implementation: Easy integration with no extra compilation.
-
Comprehensive API: Functions for loading, saving, managing configurations, adding listeners, and handling various data types.
-
Factory Design Pattern: Simplifies creating and managing instances, supporting defaults, files, and environment-specific settings.
-
Thread-Safe and Pooled Config Instances: Essential for modern apps, offering thread-safe configurations and instance pooling for better performance.
-
Minimal External Dependencies: Relies only on nlohmann/json and yaml-cpp, ensuring lightweight yet powerful functionality.
-
Boost Software License: Permissive license allows free use, modification, and distribution, even in commercial applications.
In short, I built this to offer a flexible, powerful, and user-friendly solution for both beginner and experienced C++ community members. Report issues on the repo. Contributions and feedback are welcome.
It supports many features including seamless conversion of configs between JSON and YAML, and the ability to access all environmental variables and use them in configurations.
Basic and more advanced features include loading, saving, config listeners, thread safe config creation, and managing configurations from JSON, YAML, and environment variables.
- IConfigStorage Interface: Defines the required configuration management functions.
- Config Class: Implements the IConfigStorage interface and provides configuration management functionality.
- ConfigFactory Class: Provides factory methods to create and manage Config instances.
- Templates: Handle different data types and custom format functions.
- nlohmann/json
- yaml-cpp
This repo includes a unit test in tests/ which you can build with both a CMakeLists.txt
and a Makefile
, or directly with GCC/CLang.
To build the test_configuration
in the tests/
directory, run:
make
To build with CMake, follow these steps:
mkdir -p build
cd build
cmake ..
make
To build with GCC for C++20:
g++ -std=c++20 -o test_configuration tests/test_configuration.cpp -lyaml-cpp -ljsoncpp -lc -Wall -Wextra -Wpedantic
To build with Clang for C++20:
clang++ -std=c++20 -o test_configuration tests/test_configuration.cpp -lyaml-cpp -ljsoncpp -lc -Wall -Wextra -Wpedantic
Defines the required configuration management functions.
virtual nlohmann::json get(const std::string &key) const = 0
Gets the value for a given key.
virtual void set(const std::string &key, const nlohmann::json &value) = 0
Sets the value for a given key.
virtual std::unordered_map<std::string, nlohmann::json> get_all() const = 0
Gets all key-value pairs.
virtual void load_from_file(const std::string &file_path) = 0
Loads configuration from a file.
virtual void save_to_file(const std::string &file_path) const = 0
Saves configuration to a file.
virtual void remove(const std::string &key) = 0
Removes a key-value pair.
virtual bool exists(const std::string &key) const = 0
Checks if a key exists.
virtual void clear() = 0
Clears all configurations.
virtual void load_from_file(const std::string &file_path, const std::string &version) = 0
Loads configuration from a file with a specific version.
virtual void save_to_file(const std::string &file_path, const std::string &version) const = 0
Saves configuration to a file with a specific version.
virtual void load_from_env() = 0
Loads configuration from environment variables.
virtual void add_change_listener(const std::function<void(const std::string &, const nlohmann::json &)> &listener) = 0
Adds a change listener.
virtual void backup_to_file(const std::string &backup_file_path) const = 0
Backs up the configuration to a file.
template<typename T = void> typename std::enable_if<std::is_same<T, std::ostream&>::value, std::ostream&>::type output_config(std::ostream &os) const
Outputs the configuration to an ostream.
template<typename T = void> typename std::enable_if<std::is_same<T, void>::value, void>::type output_config(std::ostream &os) const
Outputs the configuration to an ostream (void specialization).
Implements the IConfigStorage interface and provides configuration management functionality.
- Functions: Same as IConfigStorage interface, with additional functions for instance management.
Provides factory methods to create and manage Config instances.
static std::shared_ptr<Config> create_config(const std::string &name = "default")
Creates a basic config instance.
static std::shared_ptr<Config> create_new_config_from_existing(const std::string &name, const std::string &filePath)
Creates a new config instance from an existing configuration file.
static std::shared_ptr<Config> create_config_with_defaults(const std::string &name, const std::unordered_map<std::string, nlohmann::json> &defaults)
Creates a config instance with default values.
static std::shared_ptr<Config> create_env_config(const std::string &name, const std::string &environment)
Creates a config instance for a specific environment.
static std::shared_ptr<Config> create_env_loaded_config(const std::string &name)
Creates a config instance and loads it from environment variables.
static std::shared_ptr<Config> create_thread_safe_config(const std::string &name = "default")
Thread-safe method to create or get a config instance.
static std::shared_ptr<Config> get_pooled_config(const std::string &name = "default")
Pools and reuses instances.
Handle different data types and custom format functions.
#include "configuration.hpp"
using namespace config;
// Example: Create a default config instance
Config &config = Config::instance();
config.set("name", "example");
std::cout << config.get("name") << std::endl;
Expected result:
example
// Example: Load configuration from JSON file
config.load_from_file("config.json");
std::cout << config.get("name") << std::endl;
Expected result:
example
// Example: Save configuration to YAML file
config.save_to_file("config.yaml", "1.0.0");
// Example: Add change listener
bool listener_called = false;
config.add_change_listener([&listener_called](const std::string &key, const nlohmann::json &value) {
if (key == "name" && value == "example_listener") {
listener_called = true;
}
});
config.set("name", "example_listener");
Expected result:
Listener is called, listener_called is true
// Example: Load configuration from environment variables
setenv("name", "env_example", 1);
config.load_from_env();
std::cout << config.get("name") << std::endl;
Expected result:
env_example
// Example: Backup configuration to file
config.backup_to_file("backup.json");
// Example: Validate configuration
std::unordered_map<std::string, std::function<bool(const nlohmann::json &)>> validators = {
{"name", [](const nlohmann::json &value) { return value.is_string(); }},
{"age", [](const nlohmann::json &value) { return value.is_number_integer() && value > 0; }}
};
config.validate(validators);
// Example: Inspect configuration keys
std::vector<nlohmann::json> inspected = config.inspect({"name", "complex"});
for (const auto &value : inspected) {
std::cout << value.dump(4) << std::endl;
}
Expected result:
"example"
{
"key1": "value1",
"key2": 42,
"key3": {
"nestedKey": "nestedValue"
}
}
// Example: Update multiple configuration values
std::unordered_map<std::string, nlohmann::json> new_cfg = {
{"name", "new_example"},
{"age", 30}
};
config.update_multiple(new_cfg);
std::cout << config.get("name") << std::endl;
std::cout << config.get("age") << std::endl;
Expected result:
new_example
30
// Example: Partial save and load (JSON)
config.save_partial_to_file("partial_config.json", {"complex"});
config.clear();
config.load_partial_from_file("partial_config.json", {"complex"});
std::cout << config.get("complex").dump(4) << std::endl;
Expected result:
{
"key1": "value1",
"key2": 42,
"key3": {
"nestedKey": "nestedValue"
}
}
// Example: Output configuration in different formats
set_output_format(OutputFormat::JSON);
config.output_config<std::ostream&>(std::cout) << std::endl;
set_output_format(OutputFormat::YAML);
config.output_config<void>(std::cout);
Expected result:
{
"complex": {
"key1": "value1",
"key2": 42,
"key3": {
"nestedKey": "nestedValue"
}
}
}
complex:
key1: value1
key2: 42
key3:
nestedKey: value
(c) 2024, Benjamin Gorlick | github.com/bgorlick/config_manager
Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)