diff --git a/glass/src/lib/native/cpp/Storage.cpp b/glass/src/lib/native/cpp/Storage.cpp index 14d113fa295..2d91fb5cdb2 100644 --- a/glass/src/lib/native/cpp/Storage.cpp +++ b/glass/src/lib/native/cpp/Storage.cpp @@ -6,8 +6,10 @@ #include #include +#include #include #include +#include #include #include @@ -16,307 +18,65 @@ using namespace glass; -template -bool ConvertFromString(To* out, std::string_view str) { - if constexpr (std::same_as) { - if (str == "true") { - *out = true; - } else if (str == "false") { - *out = false; - } else if (auto val = wpi::parse_integer(str, 10)) { - *out = val.value() != 0; - } else { - return false; - } - } else if constexpr (std::floating_point) { - if (auto val = wpi::parse_float(str)) { - *out = val.value(); - } else { - return false; - } - } else { - if (auto val = wpi::parse_integer(str, 10)) { - *out = val.value(); - } else { - return false; - } +Storage::Value* Storage::FindValue(std::string_view key) const { + auto it = m_values.find(key); + if (it == m_values.end()) { + return nullptr; } - return true; + return &it->second; } -#define CONVERT(CapsName, LowerName, CType) \ - static bool Convert##CapsName(Storage::Value* value) { \ - switch (value->type) { \ - case Storage::Value::kBool: \ - value->LowerName##Val = value->boolVal; \ - value->LowerName##Default = value->boolDefault; \ - break; \ - case Storage::Value::kDouble: \ - value->LowerName##Val = value->doubleVal; \ - value->LowerName##Default = value->doubleDefault; \ - break; \ - case Storage::Value::kFloat: \ - value->LowerName##Val = value->floatVal; \ - value->LowerName##Default = value->floatDefault; \ - break; \ - case Storage::Value::kInt: \ - value->LowerName##Val = value->intVal; \ - value->LowerName##Default = value->intDefault; \ - break; \ - case Storage::Value::kInt64: \ - value->LowerName##Val = value->int64Val; \ - value->LowerName##Default = value->int64Default; \ - break; \ - case Storage::Value::kString: \ - if (!ConvertFromString(&value->LowerName##Val, value->stringVal)) { \ - return false; \ - } \ - if (!ConvertFromString(&value->LowerName##Default, \ - value->stringDefault)) { \ - return false; \ - } \ - break; \ - default: \ - return false; \ - } \ - value->type = Storage::Value::k##CapsName; \ - return true; \ - } - -CONVERT(Int, int, int) -CONVERT(Int64, int64, int64_t) -CONVERT(Float, float, float) -CONVERT(Double, double, double) -CONVERT(Bool, bool, bool) - -static inline bool ConvertString(Storage::Value* value) { - return false; +template +static void Set(Storage::Value& value, T&& val) { + value.data = std::forward(val); + value.dataDefault = std::forward(val); } -// Arrays can only come from JSON, so we only have to worry about conversions -// between the various number types, not bool or string - -template -static void ConvertArray(std::vector** outPtr, std::vector** inPtr) { - if (*inPtr) { - if (*outPtr) { - (*outPtr)->assign((*inPtr)->begin(), (*inPtr)->end()); - } else { - std::vector* tmp; - tmp = new std::vector{(*inPtr)->begin(), (*inPtr)->end()}; - delete *inPtr; - *outPtr = tmp; - } - } else { - *outPtr = nullptr; - } +void Storage::SetInt(std::string_view key, int val) { + Set(GetValue(key), val); } -#define CONVERT_ARRAY(CapsName, LowerName) \ - static bool Convert##CapsName##Array(Storage::Value* value) { \ - switch (value->type) { \ - case Storage::Value::kDoubleArray: \ - ConvertArray(&value->LowerName##Array, &value->doubleArray); \ - ConvertArray(&value->LowerName##ArrayDefault, \ - &value->doubleArrayDefault); \ - break; \ - case Storage::Value::kFloatArray: \ - ConvertArray(&value->LowerName##Array, &value->floatArray); \ - ConvertArray(&value->LowerName##ArrayDefault, \ - &value->floatArrayDefault); \ - break; \ - case Storage::Value::kIntArray: \ - ConvertArray(&value->LowerName##Array, &value->intArray); \ - ConvertArray(&value->LowerName##ArrayDefault, \ - &value->intArrayDefault); \ - break; \ - case Storage::Value::kInt64Array: \ - ConvertArray(&value->LowerName##Array, &value->int64Array); \ - ConvertArray(&value->LowerName##ArrayDefault, \ - &value->int64ArrayDefault); \ - break; \ - default: \ - return false; \ - } \ - value->type = Storage::Value::k##CapsName##Array; \ - return true; \ - } - -CONVERT_ARRAY(Int, int) -CONVERT_ARRAY(Int64, int64) -CONVERT_ARRAY(Float, float) -CONVERT_ARRAY(Double, double) - -static inline bool ConvertBoolArray(Storage::Value* value) { - return false; +void Storage::SetBool(std::string_view key, bool val) { + Set(GetValue(key), val); } -static inline bool ConvertStringArray(Storage::Value* value) { - return false; +void Storage::SetFloat(std::string_view key, float val) { + Set(GetValue(key), val); } -void Storage::Value::Reset(Type newType) { - switch (type) { - case kChild: - delete child; - break; - case kIntArray: - delete intArray; - delete intArrayDefault; - break; - case kInt64Array: - delete int64Array; - delete int64ArrayDefault; - break; - case kBoolArray: - delete boolArray; - delete boolArrayDefault; - break; - case kFloatArray: - delete floatArray; - delete floatArrayDefault; - break; - case kDoubleArray: - delete doubleArray; - delete doubleArrayDefault; - break; - case kStringArray: - delete stringArray; - delete stringArrayDefault; - break; - case kChildArray: - delete childArray; - break; - default: - break; - } - type = newType; +void Storage::SetDouble(std::string_view key, double val) { + Set(GetValue(key), val); } -Storage::Value* Storage::FindValue(std::string_view key) { - auto it = m_values.find(key); - if (it == m_values.end()) { - return nullptr; - } - return &it->second; +void Storage::SetString(std::string_view key, std::string_view val) { + Set(GetValue(key), std::string{val}); } -#define DEFUN(CapsName, LowerName, CType, CParamType, ArrCType) \ - CType Storage::Read##CapsName(std::string_view key, CParamType defaultVal) \ - const { \ - auto it = m_values.find(key); \ - if (it == m_values.end()) { \ - return CType{defaultVal}; \ - } \ - Value& value = it->second; \ - if (value.type != Value::k##CapsName) { \ - if (!Convert##CapsName(&value)) { \ - value.Reset(Value::k##CapsName); \ - value.LowerName##Val = defaultVal; \ - value.LowerName##Default = defaultVal; \ - value.hasDefault = true; \ - } \ - } \ - return value.LowerName##Val; \ - } \ - \ - void Storage::Set##CapsName(std::string_view key, CParamType val) { \ - auto [it, isNew] = m_values.try_emplace(key, Value::k##CapsName); \ - if (!isNew) { \ - it->second.Reset(Value::k##CapsName); \ - } \ - it->second.LowerName##Val = val; \ - it->second.LowerName##Default = {}; \ - } \ - \ - CType& Storage::Get##CapsName(std::string_view key, CParamType defaultVal) { \ - auto [it, setValue] = m_values.try_emplace(key, Value::k##CapsName); \ - if (!setValue && it->second.type != Value::k##CapsName) { \ - if (!Convert##CapsName(&it->second)) { \ - it->second.Reset(Value::k##CapsName); \ - setValue = true; \ - } \ - } \ - Value& value = it->second; \ - if (setValue) { \ - value.LowerName##Val = defaultVal; \ - } \ - if (!value.hasDefault) { \ - value.LowerName##Default = defaultVal; \ - value.hasDefault = true; \ - } \ - return value.LowerName##Val; \ - } \ - \ - std::vector& Storage::Get##CapsName##Array( \ - std::string_view key, std::span defaultVal) { \ - auto [it, setValue] = \ - m_values.try_emplace(key, Value::k##CapsName##Array); \ - if (!setValue && it->second.type != Value::k##CapsName##Array) { \ - if (!Convert##CapsName##Array(&it->second)) { \ - it->second.Reset(Value::k##CapsName##Array); \ - setValue = true; \ - } \ - } \ - Value& value = it->second; \ - if (setValue) { \ - value.LowerName##Array = \ - new std::vector{defaultVal.begin(), defaultVal.end()}; \ - } \ - if (!value.hasDefault) { \ - if (defaultVal.empty()) { \ - value.LowerName##ArrayDefault = nullptr; \ - } else { \ - value.LowerName##ArrayDefault = \ - new std::vector{defaultVal.begin(), defaultVal.end()}; \ - } \ - value.hasDefault = true; \ - } \ - assert(value.LowerName##Array); \ - return *value.LowerName##Array; \ - } - -DEFUN(Int, int, int, int, int) -DEFUN(Int64, int64, int64_t, int64_t, int64_t) -DEFUN(Bool, bool, bool, bool, int) -DEFUN(Float, float, float, float, float) -DEFUN(Double, double, double, double, double) -DEFUN(String, string, std::string, std::string_view, std::string) - Storage& Storage::GetChild(std::string_view label_id) { auto [label, id] = wpi::split(label_id, "###"); if (id.empty()) { id = label; } - Value& childValue = GetValue(id); - if (childValue.type != Value::kChild) { - childValue.Reset(Value::kChild); - childValue.child = new Storage; + Value& value = GetValue(id); + if (auto data = std::get_if(&value.data)) { + return **data; } - return *childValue.child; + return *value.data.emplace(std::make_unique()); } std::vector>& Storage::GetChildArray( std::string_view key) { - auto [it, isNew] = m_values.try_emplace(key, Value::kChildArray); - if (!isNew && it->second.type != Value::kChildArray) { - it->second.Reset(Value::kChildArray); - it->second.childArray = new std::vector>(); - } - - return *it->second.childArray; -} - -void Storage::Erase(std::string_view key) { - auto it = m_values.find(key); - if (it != m_values.end()) { - m_values.erase(it); + Value& value = GetValue(key); + if (auto data = std::get_if>(&value.data)) { + return *data; } + return value.data.emplace>(); } void Storage::EraseChildren() { - std::erase_if(m_values, - [](const auto& kv) { return kv.second.type == Value::kChild; }); + std::erase_if(m_values, [](const auto& kv) { + return std::holds_alternative(kv.second); + }); } static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::json& jarr, @@ -330,39 +90,24 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::json& jarr, // guess array type from first element switch (arr[0].type()) { case wpi::json::value_t::boolean: - if (valuePtr->type != Storage::Value::kBoolArray) { - valuePtr->Reset(Storage::Value::kBoolArray); - valuePtr->boolArray = new std::vector(); - valuePtr->boolArrayDefault = nullptr; - } - break; + ImGui::LogText("boolean array in %s, ignoring", filename); + return false; case wpi::json::value_t::number_float: - if (valuePtr->type != Storage::Value::kDoubleArray) { - valuePtr->Reset(Storage::Value::kDoubleArray); - valuePtr->doubleArray = new std::vector(); - valuePtr->doubleArrayDefault = nullptr; - } + valuePtr->data.emplace>(); + valuePtr->dataDefault = {}; break; case wpi::json::value_t::number_integer: case wpi::json::value_t::number_unsigned: - if (valuePtr->type != Storage::Value::kInt64Array) { - valuePtr->Reset(Storage::Value::kInt64Array); - valuePtr->int64Array = new std::vector(); - valuePtr->int64ArrayDefault = nullptr; - } + valuePtr->data.emplace>(); + valuePtr->dataDefault = {}; break; case wpi::json::value_t::string: - if (valuePtr->type != Storage::Value::kStringArray) { - valuePtr->Reset(Storage::Value::kStringArray); - valuePtr->stringArray = new std::vector(); - valuePtr->stringArrayDefault = nullptr; - } + valuePtr->data.emplace>(); + valuePtr->dataDefault = {}; break; case wpi::json::value_t::object: - if (valuePtr->type != Storage::Value::kChildArray) { - valuePtr->Reset(Storage::Value::kChildArray); - valuePtr->childArray = new std::vector>(); - } + valuePtr->data.emplace>(); + valuePtr->dataDefault = {}; break; case wpi::json::value_t::array: ImGui::LogText("nested array in %s, ignoring", filename); @@ -376,49 +121,48 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr, const wpi::json& jarr, for (auto jvalue : arr) { switch (jvalue.type()) { case wpi::json::value_t::boolean: - if (valuePtr->type == Storage::Value::kBoolArray) { - valuePtr->boolArray->push_back(jvalue.get()); - } else { - goto error; - } - break; + ImGui::LogText("boolean array value in %s, ignoring", filename); + return false; case wpi::json::value_t::number_float: - if (valuePtr->type == Storage::Value::kDoubleArray) { - valuePtr->doubleArray->push_back(jvalue.get()); + if (auto data = std::get_if>(&valuePtr->data)) { + data->emplace_back(jvalue.get()); } else { goto error; } break; case wpi::json::value_t::number_integer: - if (valuePtr->type == Storage::Value::kInt64Array) { - valuePtr->int64Array->push_back(jvalue.get()); - } else if (valuePtr->type == Storage::Value::kDoubleArray) { - valuePtr->doubleArray->push_back(jvalue.get()); + if (auto data = std::get_if>(&valuePtr->data)) { + data->emplace_back(jvalue.get()); + } else if (auto data = + std::get_if>(&valuePtr->data)) { + data->emplace_back(jvalue.get()); } else { goto error; } break; case wpi::json::value_t::number_unsigned: - if (valuePtr->type == Storage::Value::kInt64Array) { - valuePtr->int64Array->push_back(jvalue.get()); - } else if (valuePtr->type == Storage::Value::kDoubleArray) { - valuePtr->doubleArray->push_back(jvalue.get()); + if (auto data = std::get_if>(&valuePtr->data)) { + data->emplace_back(jvalue.get()); + } else if (auto data = + std::get_if>(&valuePtr->data)) { + data->emplace_back(jvalue.get()); } else { goto error; } break; case wpi::json::value_t::string: - if (valuePtr->type == Storage::Value::kStringArray) { - valuePtr->stringArray->emplace_back( - jvalue.get_ref()); + if (auto data = + std::get_if>(&valuePtr->data)) { + data->emplace_back(jvalue.get_ref()); } else { goto error; } break; case wpi::json::value_t::object: - if (valuePtr->type == Storage::Value::kChildArray) { - valuePtr->childArray->emplace_back(std::make_unique()); - valuePtr->childArray->back()->FromJson(jvalue, filename); + if (auto data = std::get_if>( + &valuePtr->data)) { + data->emplace_back(std::make_unique()) + ->FromJson(jvalue, filename); } else { goto error; } @@ -452,31 +196,24 @@ bool Storage::FromJson(const wpi::json& json, const char* filename) { auto& jvalue = jkv.value(); switch (jvalue.type()) { case wpi::json::value_t::boolean: - it->second.Reset(Value::kBool); - it->second.boolVal = jvalue.get(); + it->second.data = jvalue.get(); break; case wpi::json::value_t::number_float: - it->second.Reset(Value::kDouble); - it->second.doubleVal = jvalue.get(); + it->second.data = jvalue.get(); break; case wpi::json::value_t::number_integer: - it->second.Reset(Value::kInt64); - it->second.int64Val = jvalue.get(); + it->second.data = static_cast(jvalue.get()); break; case wpi::json::value_t::number_unsigned: - it->second.Reset(Value::kInt64); - it->second.int64Val = jvalue.get(); + it->second.data = static_cast(jvalue.get()); break; case wpi::json::value_t::string: - it->second.Reset(Value::kString); - it->second.stringVal = jvalue.get_ref(); + it->second.data = jvalue.get_ref(); break; case wpi::json::value_t::object: - if (it->second.type != Value::kChild) { - it->second.Reset(Value::kChild); - it->second.child = new Storage; - } - it->second.child->FromJson(jvalue, filename); // recurse + it->second.data + .emplace(std::make_unique()) + ->FromJson(jvalue, filename); // recurse break; case wpi::json::value_t::array: if (!JsonArrayToStorage(&it->second, jvalue, filename)) { @@ -496,29 +233,6 @@ bool Storage::FromJson(const wpi::json& json, const char* filename) { return true; } -template -static wpi::json StorageToJsonArray(const std::vector& arr) { - wpi::json jarr = wpi::json::array(); - for (auto&& v : arr) { - jarr.emplace_back(v); - } - return jarr; -} - -template <> -wpi::json StorageToJsonArray>( - const std::vector>& arr) { - wpi::json jarr = wpi::json::array(); - for (auto&& v : arr) { - jarr.emplace_back(v->ToJson()); - } - // remove any trailing empty items - while (!jarr.empty() && jarr.back().empty()) { - jarr.get_ref().pop_back(); - } - return jarr; -} - wpi::json Storage::ToJson() const { if (m_toJson) { return m_toJson(); @@ -526,51 +240,34 @@ wpi::json Storage::ToJson() const { wpi::json j = wpi::json::object(); for (auto&& kv : m_values) { - wpi::json jelem; - Value& value = kv.second; - switch (value.type) { -#define CASE(CapsName, LowerName) \ - case Value::k##CapsName: \ - if (value.hasDefault && \ - value.LowerName##Val == value.LowerName##Default) { \ - continue; \ - } \ - jelem = value.LowerName##Val; \ - break; \ - case Value::k##CapsName##Array: \ - if (value.hasDefault && \ - ((!value.LowerName##ArrayDefault && \ - value.LowerName##Array->empty()) || \ - (value.LowerName##ArrayDefault && \ - *value.LowerName##Array == *value.LowerName##ArrayDefault))) { \ - continue; \ - } \ - jelem = StorageToJsonArray(*value.LowerName##Array); \ - break; - - CASE(Int, int) - CASE(Int64, int64) - CASE(Bool, bool) - CASE(Float, float) - CASE(Double, double) - CASE(String, string) - - case Value::kChild: - jelem = value.child->ToJson(); // recurse - if (jelem.empty()) { - continue; - } - break; - case Value::kChildArray: - jelem = StorageToJsonArray(*value.childArray); - if (jelem.empty()) { - continue; - } - break; - default: - continue; + auto jelem = std::visit( + [&](auto&& arg) -> wpi::json { + using T = std::decay_t; + if constexpr (std::same_as) { + return arg->ToJson(); // recurse + } else if constexpr (std::same_as>) { + wpi::json jarr = wpi::json::array(); + for (auto&& v : arg) { + jarr.emplace_back(v->ToJson()); + } + // remove any trailing empty items + while (!jarr.empty() && jarr.back().empty()) { + jarr.get_ref().pop_back(); + } + return jarr; + } else if constexpr (std::same_as) { + return {}; + } else if (auto d = std::get_if(&kv.second.dataDefault); + d && *d == arg) { + return {}; + } else { + return arg; + } + }, + kv.second.data); + if (!jelem.empty()) { + j.emplace(kv.first, std::move(jelem)); } - j.emplace(kv.first, std::move(jelem)); } return j; } @@ -585,79 +282,22 @@ void Storage::Clear() { void Storage::ClearValues() { for (auto&& kv : m_values) { - Value& value = kv.second; - switch (value.type) { - case Value::kInt: - value.intVal = value.intDefault; - break; - case Value::kInt64: - value.int64Val = value.int64Default; - break; - case Value::kBool: - value.boolVal = value.boolDefault; - break; - case Value::kFloat: - value.floatVal = value.floatDefault; - break; - case Value::kDouble: - value.doubleVal = value.doubleDefault; - break; - case Value::kString: - value.stringVal = value.stringDefault; - break; - case Value::kIntArray: - if (value.intArrayDefault) { - *value.intArray = *value.intArrayDefault; - } else { - value.intArray->clear(); - } - break; - case Value::kInt64Array: - if (value.int64ArrayDefault) { - *value.int64Array = *value.int64ArrayDefault; - } else { - value.int64Array->clear(); - } - break; - case Value::kBoolArray: - if (value.boolArrayDefault) { - *value.boolArray = *value.boolArrayDefault; - } else { - value.boolArray->clear(); - } - break; - case Value::kFloatArray: - if (value.floatArrayDefault) { - *value.floatArray = *value.floatArrayDefault; - } else { - value.floatArray->clear(); - } - break; - case Value::kDoubleArray: - if (value.doubleArrayDefault) { - *value.doubleArray = *value.doubleArrayDefault; - } else { - value.doubleArray->clear(); - } - break; - case Value::kStringArray: - if (value.stringArrayDefault) { - *value.stringArray = *value.stringArrayDefault; - } else { - value.stringArray->clear(); - } - break; - case Value::kChild: - value.child->Clear(); - break; - case Value::kChildArray: - for (auto&& child : *value.childArray) { - child->Clear(); - } - break; - default: - break; - } + std::visit( + [&](auto&& arg) { + using T = std::decay_t; + if constexpr (std::same_as) { + arg->Clear(); + } else if constexpr (std::same_as>) { + for (auto&& child : arg) { + child->Clear(); + } + } else if (auto d = std::get_if(&kv.second.dataDefault)) { + arg = *d; + } else { + arg = {}; + } + }, + kv.second.data); } } @@ -672,17 +312,12 @@ void Storage::Apply() { void Storage::ApplyChildren() { for (auto&& kv : m_values) { Value& value = kv.second; - switch (value.type) { - case Value::kChild: - value.child->Apply(); - break; - case Value::kChildArray: - for (auto&& child : *value.childArray) { - child->Apply(); - } - break; - default: - break; + if (auto d = std::get_if(&value.data)) { + (*d)->Apply(); + } else if (auto d = std::get_if>(&value.data)) { + for (auto&& child : *d) { + child->Apply(); + } } } } diff --git a/glass/src/lib/native/include/glass/Storage.h b/glass/src/lib/native/include/glass/Storage.h index 518fa592c07..806f185544b 100644 --- a/glass/src/lib/native/include/glass/Storage.h +++ b/glass/src/lib/native/include/glass/Storage.h @@ -8,12 +8,15 @@ #include #include +#include #include #include #include #include +#include #include +#include #include #include #include @@ -39,84 +42,40 @@ class ChildIterator; */ class Storage { public: - struct Value { - enum Type { - kNone, - kInt, - kInt64, - kBool, - kFloat, - kDouble, - kString, - kChild, - kIntArray, - kInt64Array, - kBoolArray, - kFloatArray, - kDoubleArray, - kStringArray, - kChildArray - }; + class Value { + public: + using Child = std::unique_ptr; + using ValueData = + std::variant, std::vector, + std::vector, std::vector, + std::vector>; Value() = default; - explicit Value(Type type) : type{type} {} - Value(const Value&) = delete; - Value& operator=(const Value&) = delete; - Value(Value&& rhs) - : type{rhs.type}, - stringVal{std::move(rhs.stringVal)}, - stringDefault{std::move(rhs.stringDefault)}, - hasDefault{rhs.hasDefault} { - rhs.type = kNone; + explicit Value(ValueData&& data) : data{std::move(data)} {} + + template + R Read(T&& defaultVal) { + if (auto d = std::get_if(&data)) { + return *d; + } + if (auto d = Convert(&data)) { + Convert(&dataDefault); + return *d; + } + data.emplace(defaultVal); + dataDefault.emplace(defaultVal); + return R{std::forward(defaultVal)}; } - Value& operator=(Value&& rhs) { - Reset(kNone); - type = rhs.type; - stringVal = std::move(rhs.stringVal); - stringDefault = std::move(rhs.stringDefault); - hasDefault = rhs.hasDefault; - rhs.type = kNone; - return *this; + + template + void Set(T&& val) { + data.emplace(val); + dataDefault.emplace(std::forward(val)); } - ~Value() { Reset(kNone); } - - Type type = kNone; - union { - int intVal; - int64_t int64Val; - bool boolVal; - float floatVal; - double doubleVal; - Storage* child; - std::vector* intArray; - std::vector* int64Array; - std::vector* boolArray; - std::vector* floatArray; - std::vector* doubleArray; - std::vector* stringArray; - std::vector>* childArray; - }; - std::string stringVal; - - union { - int intDefault; - int64_t int64Default; - bool boolDefault; - float floatDefault; - double doubleDefault; - // pointers may be nullptr to indicate empty - std::vector* intArrayDefault; - std::vector* int64ArrayDefault; - std::vector* boolArrayDefault; - std::vector* floatArrayDefault; - std::vector* doubleArrayDefault; - std::vector* stringArrayDefault; - }; - std::string stringDefault; - - bool hasDefault = false; - - void Reset(Type newType); + + ValueData data; + ValueData dataDefault; }; using ValueMap = wpi::StringMap; @@ -124,16 +83,18 @@ class Storage { using ChildIterator = detail::ChildIterator; // The "Read" functions don't create or overwrite the value - int ReadInt(std::string_view key, int defaultVal = 0) const; - int64_t ReadInt64(std::string_view key, int64_t defaultVal = 0) const; - bool ReadBool(std::string_view key, bool defaultVal = false) const; - float ReadFloat(std::string_view key, float defaultVal = 0.0f) const; - double ReadDouble(std::string_view key, double defaultVal = 0.0) const; - std::string ReadString(std::string_view key, - std::string_view defaultVal = {}) const; + template + requires std::convertible_to + R Read(std::string_view key, T&& defaultVal = {}) const { + if (auto value = FindValue(key)) { + return value->Read(std::forward(defaultVal)); + } else { + return defaultVal; + } + } + void SetInt(std::string_view key, int val); - void SetInt64(std::string_view key, int64_t val); void SetBool(std::string_view key, bool val); void SetFloat(std::string_view key, float val); void SetDouble(std::string_view key, double val); @@ -142,7 +103,6 @@ class Storage { // The "Get" functions create or override the current value type. // If the value is not set, it is set to the provided default. int& GetInt(std::string_view key, int defaultVal = 0); - int64_t& GetInt64(std::string_view key, int64_t defaultVal = 0); bool& GetBool(std::string_view key, bool defaultVal = false); float& GetFloat(std::string_view key, float defaultVal = 0.0f); double& GetDouble(std::string_view key, double defaultVal = 0.0); @@ -151,10 +111,6 @@ class Storage { std::vector& GetIntArray(std::string_view key, std::span defaultVal = {}); - std::vector& GetInt64Array(std::string_view key, - std::span defaultVal = {}); - std::vector& GetBoolArray(std::string_view key, - std::span defaultVal = {}); std::vector& GetFloatArray(std::string_view key, std::span defaultVal = {}); std::vector& GetDoubleArray(std::string_view key, @@ -163,9 +119,10 @@ class Storage { std::string_view key, std::span defaultVal = {}); std::vector>& GetChildArray(std::string_view key); - Value* FindValue(std::string_view key); + Value* FindValue(std::string_view key) const; + Value& GetValue(std::string_view key) { - return m_values.try_emplace(key, Value::kNone).first->second; + return m_values.try_emplace(key).first->second; } Storage& GetChild(std::string_view label_id); @@ -192,7 +149,7 @@ class Storage { m_values.try_emplace(std::string{key}, std::move(value)); } - void Erase(std::string_view key); + void Erase(std::string_view key) { m_values.erase(key); } void EraseAll() { m_values.clear(); } @@ -260,6 +217,61 @@ class Storage { std::function m_toJson; std::function m_clear; std::function m_apply; + + template + static std::optional ParseString(std::string_view str) { + if constexpr (std::same_as) { + if (str == "true") { + return true; + } else if (str == "false") { + return false; + } else if (auto val = wpi::parse_integer(str, 10)) { + return val.value() != 0; + } else { + return std::nullopt; + } + } else if constexpr (std::floating_point) { + return wpi::parse_float(str); + } else { + return wpi::parse_integer(str, 10); + } + return std::nullopt; + } + + template + static T* Convert(Value::ValueData* data) { + if constexpr (std::same_as) { + if (auto d = std::get_if(data)) { + if (auto val = ParseString(*d)) { + return &data->emplace(*val); + } + } + } else if constexpr (std::same_as || std::same_as || + std::same_as) { + if (auto d = std::get_if(data)) { + return &data->emplace(*d); + } else if (auto d = std::get_if(data)) { + return &data->emplace(*d); + } else if (auto d = std::get_if(data)) { + return &data->emplace(*d); + } else if (auto d = std::get_if(data)) { + if (auto val = ParseString(*d)) { + return &data->emplace(*val); + } + } + } else if constexpr (std::same_as> || + std::same_as> || + std::same_as>) { + if (auto d = std::get_if>(data)) { + return &data->emplace(T{d->begin(), d->end()}); + } else if (auto d = std::get_if>(data)) { + return &data->emplace(T{d->begin(), d->end()}); + } else if (auto d = std::get_if>(data)) { + return &data->emplace(T{d->begin(), d->end()}); + } + } + return nullptr; + } }; namespace detail { @@ -275,7 +287,8 @@ class ChildIterator { public: ChildIterator(IteratorType it, IteratorType end) noexcept : anchor(it), end(end) { - while (anchor != end && anchor->second.type != Storage::Value::kChild) { + while (anchor != end && !std::holds_alternative( + anchor->second.data)) { ++anchor; } } @@ -286,7 +299,8 @@ class ChildIterator { /// increment operator (needed for range-based for) ChildIterator& operator++() { ++anchor; - while (anchor != end && anchor->second.type != Storage::Value::kChild) { + while (anchor != end && !std::holds_alternative( + anchor->second.data)) { ++anchor; } return *this; @@ -301,7 +315,9 @@ class ChildIterator { std::string_view key() const { return anchor->first; } /// return value of the iterator - Storage& value() const { return *anchor->second.child; } + Storage& value() const { + return *std::get(anchor->second.data); + } }; } // namespace detail