Skip to content

Instantly share code, notes, and snippets.

@AlexandreGerault
Last active June 2, 2020 10:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlexandreGerault/1410736797d59181c0ff62b2eba90b21 to your computer and use it in GitHub Desktop.
Save AlexandreGerault/1410736797d59181c0ff62b2eba90b21 to your computer and use it in GitHub Desktop.
#include <core/configuration.hpp>
configuration::configuration() : m_file("default.config") {
}
configuration::configuration(std::string path) : m_file(std::move(path)) {}
/**
* Check whether the options map contains a key or not
*
* @param key
* @return true if the map has the option
*/
bool configuration::has(const std::string &key) const {
return m_fields.contains(key);
}
/**
* Update the value of an existing option identified by its key.
* If the key does not exist then the function does nothing
*
* @param key
* @param value
*/
void configuration::set(const std::string &key, const std::string &value) {
auto iterator = m_fields.find(key);
if (iterator != m_fields.end()) {
iterator->second = value;
}
}
/**
* Add a new option entry if the entry does not still exist
* Else it throw an exception
*
* @param key
* @param value
*/
void configuration::add(const std::string &key, const std::string &value) {
m_fields.insert({key, value});
}
/**
* Remove an option if it finds it.
* Else it does nothing.
*
* @param key
*/
void configuration::remove(const std::string &key) { m_fields.erase(key); }
/**
* Reset the map and fill it with the content of a configuration file
* The syntax of the configuration file must be like this
* KEY=VALUE
* with 1 pair per line
*
* @param path
* @return
*/
bool configuration::load() {
m_fields.clear();
std::ifstream loadingFile(m_file);
if (loadingFile.is_open()) {
std::string line;
std::string delimiter{"="};
while (std::getline(loadingFile, line)) {
if (auto pos = line.find(delimiter); pos != std::string::npos) {
std::string key{line.substr(0, pos)};
std::string value{line.substr(pos + 1, line.length())};
m_fields.insert({key, value});
}
}
return true;
}
return false;
}
/**
* Saves the current fields to a configuration file.
* If the file already exists it is previously cleared.
* It stores each option on a new line matching the below syntax:
* KEY=VALUE
*
* @param path
* @return whether the configuration was successfully saved
*/
bool configuration::save() const {
std::ofstream savingFile(m_file);
savingFile.clear();
for (auto const &[key, value] : m_fields) {
savingFile << key << "=" << value << std::endl;
}
savingFile.close();
return !savingFile.fail();
}
#ifndef CONFIGURATION_HPP
#define CONFIGURATION_HPP
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
template <typename T> struct expected {
static char const *name() { return "SHOULD NOT BE USED"; }
};
template <> struct expected<std::string> {
static char const *name() { return "a string"; }
};
template <> struct expected<int> {
static char const *name() { return "an int"; }
};
template <> struct expected<double> {
static char const *name() { return "a double"; }
};
template <typename T> struct decoder {
static T decode(std::string const &text, std::string const &name) {
std::istringstream iss(text);
if (T value; (iss >> value).eof()) {
return value;
}
throw std::runtime_error("Cannot extract parameter " + name + " as " +
expected<T>::name() + " from \"" + text + "\"");
}
};
template <> struct decoder<std::string> {
static std::string const &decode(std::string const &text,
std::string const &name) {
return text;
}
};
class configuration {
public:
configuration();
explicit configuration(std::string path);
bool has(const std::string &key) const;
void set(const std::string &key, const std::string &value);
void add(const std::string &key, const std::string &value);
void remove(const std::string &key);
bool load();
bool save() const;
/**
* Get a value from the map and convert to T it if possible.
* If it cannot convert it returns the string.
* If it doesn't find the option it does nothing.
*
* @tparam T
* @param key
* @return a converted value
*/
template <typename T>
T get(const std::string &key, T defaultValue = T{}) const {
auto iterator = m_fields.find(key);
if (iterator == m_fields.end()) {
return defaultValue;
}
return decoder<T>::decode(iterator->second, key);
};
private:
std::unordered_map<std::string, std::string> m_fields;
std::string m_file;
};
#endif // CONFIGURATION_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment