At time of writing, the default IClinet::wait_for_bytes method has the following block of code for selecting the polling interval
|
const auto poll_interval = std::chrono::milliseconds( |
|
(poll_interval_s = std::getenv(RADEX_POLL_INTERVAL_VAR.c_str())) |
|
? std::stoul(poll_interval_s) |
|
: RADEX_DEFAULT_POLL_INTERVAL_MILLISECONDS); |
This is functional, but problematic for a couple of reasons:
- It embeds configuration into buisness logic of the library, crossing concerns
- It becomes difficult for us to add a new lookup location (e.g. config file) for polling interval
- Everywhere that needs to uses a config must contain similar logic which could become problematic if we find a bug in how we retrieve a value, causing us to need to visit many touch points
I purpose adding a radex::configuration namespace specifically for dealing with the look up of these values. A VERY ROUGH implementation of what I'm thinking might look something like this:
namespace radex::configuration {
template <typename>
inline constexpr bool unsupported_type_v = false;
template <typename T>
class ILookUp {
public:
std::optional<T> get() const noexcept = 0;
};
template <typename T>
class FromEnvironment: public ILookUp {
private:
std::string env_var;
public:
FromEnvironment(std::string env_var): env_var(std::move(env_var)) {}
std::optional<T> get() const noexcept override {
const char *val = std::getenv(env_var.c_str());
if (val == nullptr) return std::nullopt;
if constexpr (std::is_same_v<T, std::chrono::milliseconds>) {
const unsigned long num = std std::stoul(val);
return std::chrono::milliseconds(num);
}
static_assert(unsupported_type_v<T>, "Don't know how to parse that type");
}
};
template <typename T>
class ConfigValueLookUp {
private:
std::vector<ILookUp<T>> look_up_locations;
T fallback;
public:
ConfigValueLookUp(std::vector<ILookUp<T>> look_up_locations, T fallback)
: look_up_locations(std::move(look_up_locations))
, fallback {fallback} {};
T find() noexcept {
for (auto const& loc: look_up_locations) {
const T val = loc.get();
if (loc) return *loc;
}
return fallback;
}
};
static inline ConfigValueLookUp<std::chrono::milliseconds>
RADEX_DEFAULT_POLL_INTERVAL {
{FromEnvironment("RADEX_POLL_INTERVAL")}, 100ms };
}
I cannot stress how much of that code block is speculative pseudo-code 😅
But hopefully it sends a good idea of what I'm thinking. Anything that moves look up of config variables out of business logic is sufficient!
At time of writing, the default
IClinet::wait_for_bytesmethod has the following block of code for selecting the polling intervalradex/src/cpp/client.cpp
Lines 63 to 66 in a67478b
This is functional, but problematic for a couple of reasons:
I purpose adding a
radex::configurationnamespace specifically for dealing with the look up of these values. A VERY ROUGH implementation of what I'm thinking might look something like this:I cannot stress how much of that code block is speculative pseudo-code 😅
But hopefully it sends a good idea of what I'm thinking. Anything that moves look up of config variables out of business logic is sufficient!