I'd rework it a bit. You probably don't want to read the configuration every time you instantiate a client
- or re-read the configuration for any other object that needs some parameter from the configuration file. Read it once and use it many times.
With most Json libraries you can also add parsers for your own types, which makes it easy to get the data out into a format that doesn't need to be parsed and re-parsed every time you need something from it. Here's an example using the Json for Modern C++ library.
Say your config.json
looks like this:
{
"application": "My soft config",
"db_buf_size": 2048
}
And you want this available to your application stored in a
struct config_data {
std::string application;
std::size_t db_buf_size;
};
You'd simply add a from_json
function to make the library able to parse it:
void from_json(const nlohmann::json& obj, config_data& c) {
c.application = obj.at("application").get<std::string>();
c.db_buf_size = obj.at("db_buf_size").get<std::size_t>();
}
You also need a function that you can call at any time that returns a reference to the loaded configuration and here's an example of a function named config()
that returns a reference to a config_data
. Since the config_data
instance is static
it will only read and parse the configuration from file once, no matter how many calls to config()
you do:
#include <cerrno>
#include <cstring>
#include <filesystem>
#include <stdexcept>
const config_data& config() {
static const config_data conf = [] {
std::filesystem::path path = "config.json";
std::ifstream ifs(path);
if(!ifs)
throw std::runtime_error(path.string() + ": " +
std::strerror(errno));
auto j = json::parse(ifs);
return j.get<config_data>(); // using the user defined parser
}();
return conf;
}
You can now let the client
constructor call config()
and fetch any value from the configuration. Again, since the actual config_data
instance in the config()
function is static
, actually loading and parsing the data will only done once.
class doubleBuffer {
public:
doubleBuffer(size_t size) : m_size(size), m_buf{new uint8_t[m_size]} {}
doubleBuffer(const doubleBuffer&) = delete;
doubleBuffer(doubleBuffer&&) = delete;
~doubleBuffer() { delete[] m_buf; }
size_t size() const { return m_size; }
private:
size_t m_size = 0;
uint8_t* m_buf = nullptr;
};
class client {
public:
client() : m_db(config().db_buf_size) {} // fetch the needed data
size_t size() const { return m_db.size(); }
private:
doubleBuffer m_db;
};
You may also want to consider using a std::vector<uint8_t>
instead of a raw uint8_t*
.