I'm starting a new project, and it needs to be set up using YAML files.
To handle all the settings, I've created a singleton class, I'd like you guys to review.
Here is the header:
#pragma once
#include <memory>
#include <string>
struct Settings {
struct General {
bool verbose;
bool debug;
} general;
struct Server {
uint16_t port;
} server;
struct SGX {
bool enabled;
struct RemoteAttestation {
bool enabled;
std::string server;
uint16_t port;
bool is_linkable;
bool use_rdrand;
bool quote_only;
std::string nonce;
std::string pubkey;
std::string spid;
bool epid_mode;
} remote_attestation;
} sgx;
};
class SettingsManager {
private:
Settings s {
.general = {
.verbose = false,
.debug = true
},
.server {
.port = 8080
},
.sgx = {
.enabled = true,
.remote_attestation {
.enabled = true,
.server = "localhost",
.port = 7777,
.is_linkable = true,
.use_rdrand = true ,
.quote_only = false,
.nonce = "" ,
.pubkey = "",
.spid = "",
.epid_mode = false,
}
}
};
bool open_and_parse(std::string file);
SettingsManager() {} // Disallow instantiation outside of the class.
public:
SettingsManager(const SettingsManager&) = delete;
SettingsManager& operator=(const SettingsManager &) = delete;
SettingsManager(SettingsManager &&) = delete;
SettingsManager & operator=(SettingsManager &&) = delete;
void print();
static std::shared_ptr<SettingsManager> instance(const std::string file) {
static std::shared_ptr<SettingsManager> instance(new SettingsManager());
if (instance->open_and_parse(file) == false)
instance = nullptr;
return instance;
}
const Settings& settings = s;
};
And the implementation:
#include "SettingsManager.h"
#include <fstream>
#include <streambuf>
#include <yaml-cpp/yaml.h>
#include <fmt/core.h>
bool SettingsManager::open_and_parse(const std::string file) {
// read file content to buffer
YAML::Node config = YAML::LoadFile(file);
s.general.debug = config["settings"]["general"]["debug"].as<bool>();
s.general.verbose = config["settings"]["general"]["verbose"].as<bool>();
s.server.port = config["settings"]["server"]["port"].as<uint16_t>();
s.sgx.enabled = config["settings"]["sgx"]["enabled"].as<bool>();
s.sgx.remote_attestation.enabled = config["settings"]["sgx"]["remote_attestation"]["enabled"].as<bool>();
s.sgx.remote_attestation.server = config["settings"]["sgx"]["remote_attestation"]["server"].as<std::string>();
s.sgx.remote_attestation.port = config["settings"]["sgx"]["remote_attestation"]["port"].as<uint16_t>();
s.sgx.remote_attestation.is_linkable = config["settings"]["sgx"]["remote_attestation"]["is_linkable"].as<bool>();
s.sgx.remote_attestation.use_rdrand = config["settings"]["sgx"]["remote_attestation"]["use_rdrand"].as<bool>();
s.sgx.remote_attestation.quote_only = config["settings"]["sgx"]["remote_attestation"]["quote_only"].as<bool>();
s.sgx.remote_attestation.nonce = config["settings"]["sgx"]["remote_attestation"]["nonce"].as<std::string>();
s.sgx.remote_attestation.pubkey = config["settings"]["sgx"]["remote_attestation"]["pubkey"].as<std::string>();
s.sgx.remote_attestation.spid = config["settings"]["sgx"]["remote_attestation"]["spid"].as<std::string>();
s.sgx.remote_attestation.epid_mode = config["settings"]["sgx"]["remote_attestation"]["epid_mode"].as<bool>();
return true;
}
void SettingsManager::print() {
fmt::print("Settings:\n");
auto bool_to_symbol {
[](bool value) -> std::string {
if (value == true)
return "✅";
else
return "❌";
}
};
// print all the settings
}
I know singletons are considered as a bad practice, but it really helps making everything clean. Oh and yes, I hate naming a class "...Manager" too.
In advance, thanks everyone.
SettingsManager::print()
? \$\endgroup\$bool_to_symbol
in the print function. \$\endgroup\$