diff --git a/src/valueprovider.cpp b/src/valueprovider.cpp index d5a3b59..7c9118f 100644 --- a/src/valueprovider.cpp +++ b/src/valueprovider.cpp @@ -15,12 +15,11 @@ MultiValueProvider::~MultiValueProvider() } void MultiValueProvider::setupValues(std::vector const& identifiers) { - for (auto sv : identifiers) { - std::string s{sv}; + for (auto s : identifiers) { auto const [it, inserted] = bass_provided_values.insert({s, this}); auto const mvp = it->second; - if (it->second != this) { + if (mvp != this) { //assert(inserted == false); throw std::runtime_error( fmt::format("{} cannot register an already registered identifier '{}' which is already registered by {})", @@ -30,11 +29,24 @@ void MultiValueProvider::setupValues(std::vector const& identifiers ); } - // initialize value + // initialize value (always a 'None' value) vals[s]; } } +void MultiValueProvider::setupRuntimeValue(std::string const& identifier) +{ + auto const [it, inserted] = bass_provided_values.insert({identifier, this}); + auto const mvp = it->second; + + if (inserted) { + // initialize value (always a 'None' value) + vals[identifier]; + } else { + throw std::runtime_error(fmt::format("Symbol '{}' already defined", identifier)); + } +} + bool MultiValueProvider::hasAllValues() const { for (auto it : bass_provided_values) { diff --git a/src/valueprovider.h b/src/valueprovider.h index bee1fe7..9bd6751 100644 --- a/src/valueprovider.h +++ b/src/valueprovider.h @@ -101,6 +101,7 @@ class MultiValueProvider virtual ~MultiValueProvider(); void setupValues(std::vector const& identifiers); + void setupRuntimeValue(std::string const& identifier); inline bool hasValue(std::string_view identifier) const { std::string s{identifier}; @@ -113,25 +114,38 @@ class MultiValueProvider bool hasAllValues() const; - - template - inline void setValue(std::string_view key, std::optional const& val) + template + inline void setValue(std::string_view key, std::optional const& opt_val) { + // NOTE: prevent (accidental) nested std::optional's (as good as possible) + // TODO: std::optional is a class template (requires our own Optional implementation) + static_assert(false == std::is_same_v>); + + // TODO: std::variant is a class template (requires our own Variant implementation for RTTI) + static_assert(false == std::is_same_v); + std::string s{key}; if (!vals.contains(s)) { throw std::runtime_error(fmt::format("{} does not register value identifier '{}'", __owner_classname__, key)); } - if (val.has_value()) { - vals[s] = val.value(); + if (opt_val.has_value()) { + vals[s] = opt_val.value(); } else if (vals.contains(s)) { LOGD(fmt::format("Invalidating value {}", key)); vals[s].reset(); } } - template + template inline T getValue(std::string_view key) { + // NOTE: prevent (accidental) nested std::optional's (as good as possible) + // TODO: std::optional is a class template (requires our own Optional implementation) + static_assert(false == std::is_same_v>); + + // TODO: std::variant is a class template (requires our own Variant implementation for RTTI) + static_assert(false == std::is_same_v); + std::string s{key}; auto const& val = vals[s]; if (val.has_value()) { diff --git a/src/valueprovidertest.cpp b/src/valueprovidertest.cpp index ef11b92..4391eaf 100644 --- a/src/valueprovidertest.cpp +++ b/src/valueprovidertest.cpp @@ -1,9 +1,7 @@ -#include "doctest.h" #include "defines.h" - #ifdef USE_BASS_VALUEPROVIDER - -#include "test_utils.h" +#include "doctest.h" +//#include "test_utils.h" #include "valueprovider.cpp" // FIXME: find better way @@ -12,6 +10,7 @@ using namespace std::string_literals; TEST_CASE("multivalueprovider.base") { MultiValueProvider mvp("TestMultiValueProvider"s); + mvp.setupValues({ "valueprovider.example.value_1"s, "valueprovider.example.value_2"s, @@ -23,12 +22,43 @@ TEST_CASE("multivalueprovider.base") TEST_CASE("multivalueprovider.fails_with_unregistered_value") { MultiValueProvider mvp("TestMultiValueProvider"s); + mvp.setupValues({ "valueprovider.example.value_1"s, "valueprovider.example.value_2"s, }); - REQUIRE_THROWS(mvp.setValue("nope"s, {"i am an unregistered value"})); + REQUIRE_THROWS( + mvp.setValue("nope"s, "i am an unregistered value"s) + ); +} + +TEST_CASE("multivalueprovider.insert_runtime_value") +{ + MultiValueProvider mvp("TestMultiValueProvider"s); + + mvp.setupValues({ + "valueprovider.example.value_1"s, + "valueprovider.example.value_2"s, + }); + + // simulates an "accessed/undefined" label during parsing + std::string test_symbol_0{"symbol_0"}; + mvp.setupRuntimeValue(test_symbol_0); // None + + mvp.setValue(test_symbol_0, {}); // still None + REQUIRE_FALSE(mvp.hasValue(test_symbol_0)); + REQUIRE_THROWS(mvp.getValue(test_symbol_0)); + + mvp.setValue(test_symbol_0, 42); // 42 (first assignement) + REQUIRE(mvp.hasValue(test_symbol_0)); + REQUIRE_EQ(mvp.getValue(test_symbol_0), 42); + + // NOTE: currently re-assigning is allowed to ease transition + // TODO: fail (meaning: "Symbol::finished == true") + int32_t fourty_three = 43; + mvp.setValue(test_symbol_0, std::optional{fourty_three}); // outputs "value runtime.value_0 changed 42 -> 43" + REQUIRE_EQ(mvp.getValue(test_symbol_0), 43); } #endif