diff --git a/datalogtool/src/main/native/cpp/App.cpp b/datalogtool/src/main/native/cpp/App.cpp index 31512a0bd12..004dfc4450b 100644 --- a/datalogtool/src/main/native/cpp/App.cpp +++ b/datalogtool/src/main/native/cpp/App.cpp @@ -154,7 +154,7 @@ void Application(std::string_view saveDir) { gui::Initialize("Datalog Tool", 925, 510); gDownloadVisible = - &glass::GetStorageRoot().GetChild("download").GetBool("visible", true); + &glass::GetStorageRoot().GetChild("download").Get("visible", true); gui::Main(); diff --git a/datalogtool/src/main/native/cpp/Downloader.cpp b/datalogtool/src/main/native/cpp/Downloader.cpp index 84b186db2bf..f3ed0d92615 100644 --- a/datalogtool/src/main/native/cpp/Downloader.cpp +++ b/datalogtool/src/main/native/cpp/Downloader.cpp @@ -29,11 +29,11 @@ #include "Sftp.h" Downloader::Downloader(glass::Storage& storage) - : m_serverTeam{storage.GetString("serverTeam")}, - m_remoteDir{storage.GetString("remoteDir", "/home/lvuser/logs")}, - m_username{storage.GetString("username", "lvuser")}, - m_localDir{storage.GetString("localDir")}, - m_deleteAfter{storage.GetBool("deleteAfter", true)}, + : m_serverTeam{storage.Get("serverTeam")}, + m_remoteDir{storage.Get("remoteDir", "/home/lvuser/logs")}, + m_username{storage.Get("username", "lvuser")}, + m_localDir{storage.Get("localDir")}, + m_deleteAfter{storage.Get("deleteAfter", true)}, m_thread{[this] { ThreadMain(); }} {} Downloader::~Downloader() { diff --git a/datalogtool/src/main/native/cpp/Exporter.cpp b/datalogtool/src/main/native/cpp/Exporter.cpp index a1213f40b62..45f8c0e19d6 100644 --- a/datalogtool/src/main/native/cpp/Exporter.cpp +++ b/datalogtool/src/main/native/cpp/Exporter.cpp @@ -618,7 +618,7 @@ static void ExportCsv(std::string_view outputFolder, int style) { } void DisplayOutput(glass::Storage& storage) { - static std::string& outputFolder = storage.GetString("outputFolder"); + static std::string& outputFolder = storage.Get("outputFolder"); static std::unique_ptr outputFolderSelector; SetNextWindowPos(ImVec2{380, 390}, ImGuiCond_FirstUseEver); diff --git a/glass/src/app/native/cpp/main.cpp b/glass/src/app/native/cpp/main.cpp index 63715b25780..676228c9897 100644 --- a/glass/src/app/native/cpp/main.cpp +++ b/glass/src/app/native/cpp/main.cpp @@ -353,7 +353,7 @@ int main(int argc, char** argv) { gui::Initialize("Glass - DISCONNECTED", 1024, 768, ImGuiConfigFlags_DockingEnable); - gEnterKey = &glass::GetStorageRoot().GetInt("enterKey", GLFW_KEY_ENTER); + gEnterKey = &glass::GetStorageRoot().Get("enterKey", GLFW_KEY_ENTER); if (auto win = gui::GetSystemWindow()) { gPrevKeyCallback = glfwSetKeyCallback(win, RemapEnterKeyCallback); } diff --git a/glass/src/lib/native/cpp/Context.cpp b/glass/src/lib/native/cpp/Context.cpp index f2aada6d834..b378d66295c 100644 --- a/glass/src/lib/native/cpp/Context.cpp +++ b/glass/src/lib/native/cpp/Context.cpp @@ -483,7 +483,7 @@ void glass::EndChild() { } bool glass::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) { - bool& open = GetStorage().GetChild(label).GetBool( + bool& open = GetStorage().GetChild(label).Get( "open", (flags & ImGuiTreeNodeFlags_DefaultOpen) != 0); ImGui::SetNextItemOpen(open); open = ImGui::CollapsingHeader(label, flags); @@ -492,7 +492,7 @@ bool glass::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) { bool glass::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) { PushStorageStack(label); - bool& open = GetStorage().GetBool( + bool& open = GetStorage().Get( "open", (flags & ImGuiTreeNodeFlags_DefaultOpen) != 0); ImGui::SetNextItemOpen(open); open = ImGui::TreeNodeEx(label, flags); diff --git a/glass/src/lib/native/cpp/DataSource.cpp b/glass/src/lib/native/cpp/DataSource.cpp index 372d5f2ee5d..4018330a168 100644 --- a/glass/src/lib/native/cpp/DataSource.cpp +++ b/glass/src/lib/native/cpp/DataSource.cpp @@ -4,6 +4,8 @@ #include "glass/DataSource.h" +#include + #include #include "glass/ContextInternal.h" @@ -13,7 +15,7 @@ using namespace glass; wpi::sig::Signal DataSource::sourceCreated; DataSource::DataSource(std::string_view id) - : m_id{id}, m_name{gContext->sourceNameStorage.GetString(m_id)} { + : m_id{id}, m_name{gContext->sourceNameStorage.Get(m_id)} { gContext->sources.try_emplace(m_id, this); sourceCreated(m_id.c_str(), this); } diff --git a/glass/src/lib/native/cpp/Storage.cpp b/glass/src/lib/native/cpp/Storage.cpp index d0009937c4b..f8b26e1baa5 100644 --- a/glass/src/lib/native/cpp/Storage.cpp +++ b/glass/src/lib/native/cpp/Storage.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -16,331 +17,21 @@ 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; - } - } - return true; -} - -#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; -} - -// 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; - } -} - -#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; -} - -static inline bool ConvertStringArray(Storage::Value* value) { - return false; -} - -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; -} - -Storage::Value* Storage::FindValue(std::string_view key) { - auto it = m_values.find(key); - if (it == m_values.end()) { - return nullptr; - } - return it->second.get(); -} - -Storage::Value& Storage::GetValue(std::string_view key) { - auto& val = m_values[key]; - if (!val) { - val = std::make_unique(); - } - return *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& valuePtr = m_values[key]; \ - if (!valuePtr) { \ - valuePtr = std::make_unique(Value::k##CapsName); \ - } else { \ - valuePtr->Reset(Value::k##CapsName); \ - } \ - valuePtr->LowerName##Val = val; \ - valuePtr->LowerName##Default = {}; \ - } \ - \ - CType& Storage::Get##CapsName(std::string_view key, CParamType defaultVal) { \ - auto& valuePtr = m_values[key]; \ - bool setValue = false; \ - if (!valuePtr) { \ - valuePtr = std::make_unique(Value::k##CapsName); \ - setValue = true; \ - } else if (valuePtr->type != Value::k##CapsName) { \ - if (!Convert##CapsName(valuePtr.get())) { \ - valuePtr->Reset(Value::k##CapsName); \ - setValue = true; \ - } \ - } \ - if (setValue) { \ - valuePtr->LowerName##Val = defaultVal; \ - } \ - if (!valuePtr->hasDefault) { \ - valuePtr->LowerName##Default = defaultVal; \ - valuePtr->hasDefault = true; \ - } \ - return valuePtr->LowerName##Val; \ - } \ - \ - std::vector& Storage::Get##CapsName##Array( \ - std::string_view key, std::span defaultVal) { \ - auto& valuePtr = m_values[key]; \ - bool setValue = false; \ - if (!valuePtr) { \ - valuePtr = std::make_unique(Value::k##CapsName##Array); \ - setValue = true; \ - } else if (valuePtr->type != Value::k##CapsName##Array) { \ - if (!Convert##CapsName##Array(valuePtr.get())) { \ - valuePtr->Reset(Value::k##CapsName##Array); \ - setValue = true; \ - } \ - } \ - if (setValue) { \ - valuePtr->LowerName##Array = \ - new std::vector{defaultVal.begin(), defaultVal.end()}; \ - } \ - if (!valuePtr->hasDefault) { \ - if (defaultVal.empty()) { \ - valuePtr->LowerName##ArrayDefault = nullptr; \ - } else { \ - valuePtr->LowerName##ArrayDefault = \ - new std::vector{defaultVal.begin(), defaultVal.end()}; \ - } \ - valuePtr->hasDefault = true; \ - } \ - assert(valuePtr->LowerName##Array); \ - return *valuePtr->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; } - auto& childPtr = m_values[id]; - if (!childPtr) { - childPtr = std::make_unique(); - } - if (childPtr->type != Value::kChild) { - childPtr->Reset(Value::kChild); - childPtr->child = new Storage; + Value& value = GetValue(id); + if (auto data = std::get_if(&value.data)) { + return **data; } - return *childPtr->child; -} - -std::vector>& Storage::GetChildArray( - std::string_view key) { - auto& valuePtr = m_values[key]; - if (!valuePtr) { - valuePtr = std::make_unique(Value::kChildArray); - valuePtr->childArray = new std::vector>(); - } else if (valuePtr->type != Value::kChildArray) { - valuePtr->Reset(Value::kChildArray); - valuePtr->childArray = new std::vector>(); - } - - return *valuePtr->childArray; -} - -std::unique_ptr Storage::Erase(std::string_view key) { - auto it = m_values.find(key); - if (it != m_values.end()) { - auto rv = std::move(it->second); - m_values.erase(it); - return rv; - } - return nullptr; + return *value.data.emplace(std::make_shared()); } void Storage::EraseChildren() { std::erase_if(m_values, [](const auto& kv) { - return kv.second->type == Value::kChild; + return std::holds_alternative(kv.second.data); }); } @@ -355,39 +46,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); @@ -401,49 +77,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_shared()) + ->FromJson(jvalue, filename); } else { goto error; } @@ -473,52 +148,43 @@ bool Storage::FromJson(const wpi::json& json, const char* filename) { return false; } for (auto&& jkv : json.items()) { - auto& valuePtr = m_values[jkv.key()]; - bool created = false; - if (!valuePtr) { - valuePtr = std::make_unique(); - created = true; - } + auto [it, created] = m_values.try_emplace(jkv.key()); auto& jvalue = jkv.value(); switch (jvalue.type()) { case wpi::json::value_t::boolean: - valuePtr->Reset(Value::kBool); - valuePtr->boolVal = jvalue.get(); + it->second.data = jvalue.get(); break; case wpi::json::value_t::number_float: - valuePtr->Reset(Value::kDouble); - valuePtr->doubleVal = jvalue.get(); + it->second.data = jvalue.get(); break; case wpi::json::value_t::number_integer: - valuePtr->Reset(Value::kInt64); - valuePtr->int64Val = jvalue.get(); + it->second.data = static_cast(jvalue.get()); break; case wpi::json::value_t::number_unsigned: - valuePtr->Reset(Value::kInt64); - valuePtr->int64Val = jvalue.get(); + it->second.data = static_cast(jvalue.get()); break; case wpi::json::value_t::string: - valuePtr->Reset(Value::kString); - valuePtr->stringVal = jvalue.get_ref(); + it->second.data = jvalue.get_ref(); break; case wpi::json::value_t::object: - if (valuePtr->type != Value::kChild) { - valuePtr->Reset(Value::kChild); - valuePtr->child = new Storage; + if (auto d = std::get_if(&it->second.data)) { + (*d)->FromJson(jvalue, filename); // recurse + } else { + it->second.data.emplace(std::make_shared()) + ->FromJson(jvalue, filename); // recurse } - valuePtr->child->FromJson(jvalue, filename); // recurse break; case wpi::json::value_t::array: - if (!JsonArrayToStorage(valuePtr.get(), jvalue, filename)) { + if (!JsonArrayToStorage(&it->second, jvalue, filename)) { if (created) { - m_values.erase(jkv.key()); + m_values.erase(it); } } break; default: ImGui::LogText("null value in %s, ignoring", filename); if (created) { - m_values.erase(jkv.key()); + m_values.erase(it); } break; } @@ -526,29 +192,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(); @@ -556,163 +199,62 @@ wpi::json Storage::ToJson() const { wpi::json j = wpi::json::object(); for (auto&& kv : m_values) { - wpi::json jelem; - auto& 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; } -void Storage::Clear() { - if (m_clear) { - return m_clear(); - } - - ClearValues(); -} - void Storage::ClearValues() { for (auto&& kv : m_values) { - auto& 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; + Value& value = kv.second; + if (auto d = std::get_if(&value.data)) { + (*d)->Clear(); + } else if (auto d = std::get_if>(&value.data)) { + for (auto&& child : *d) { + child->Clear(); + } + } else { + value.data = value.dataDefault; } } } -void Storage::Apply() { - if (m_apply) { - return m_apply(); - } - - ApplyChildren(); -} - void Storage::ApplyChildren() { for (auto&& kv : m_values) { - auto& 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; + Value& value = kv.second; + 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/cpp/Window.cpp b/glass/src/lib/native/cpp/Window.cpp index 64b043ffb1e..7092fed709f 100644 --- a/glass/src/lib/native/cpp/Window.cpp +++ b/glass/src/lib/native/cpp/Window.cpp @@ -19,12 +19,13 @@ using namespace glass; Window::Window(Storage& storage, std::string_view id, Visibility defaultVisibility) : m_id{id}, - m_name{storage.GetString("name")}, + m_name{storage.Get("name")}, m_defaultName{id}, - m_visible{storage.GetBool("visible", defaultVisibility != kHide)}, - m_enabled{storage.GetBool("enabled", defaultVisibility != kDisabled)}, - m_defaultVisible{storage.GetValue("visible").boolDefault}, - m_defaultEnabled{storage.GetValue("enabled").boolDefault} {} + m_visible{storage.Get("visible", defaultVisibility != kHide)}, + m_enabled{storage.Get("enabled", defaultVisibility != kDisabled)}, + m_defaultVisible{std::get(storage.GetValue("visible").dataDefault)}, + m_defaultEnabled{ + std::get(storage.GetValue("enabled").dataDefault)} {} void Window::SetVisibility(Visibility visibility) { m_visible = visibility != kHide; diff --git a/glass/src/lib/native/cpp/hardware/AnalogInput.cpp b/glass/src/lib/native/cpp/hardware/AnalogInput.cpp index 8c45f011386..6d61c5c8d1e 100644 --- a/glass/src/lib/native/cpp/hardware/AnalogInput.cpp +++ b/glass/src/lib/native/cpp/hardware/AnalogInput.cpp @@ -22,7 +22,7 @@ void glass::DisplayAnalogInput(AnalogInputModel* model, int index) { } // build label - std::string& name = GetStorage().GetString("name"); + std::string& name = GetStorage().Get("name"); char label[128]; if (!name.empty()) { wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###name", name, index); diff --git a/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp b/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp index 11790f8ca8d..a5122ac107c 100644 --- a/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp +++ b/glass/src/lib/native/cpp/hardware/AnalogOutput.cpp @@ -31,7 +31,7 @@ void glass::DisplayAnalogOutputsDevice(AnalogOutputsModel* model) { PushID(i); // build label - std::string& name = GetStorage().GetString("name"); + std::string& name = GetStorage().Get("name"); char label[128]; if (!name.empty()) { wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###name", name, i); diff --git a/glass/src/lib/native/cpp/hardware/Encoder.cpp b/glass/src/lib/native/cpp/hardware/Encoder.cpp index b74547c735a..05d216bae10 100644 --- a/glass/src/lib/native/cpp/hardware/Encoder.cpp +++ b/glass/src/lib/native/cpp/hardware/Encoder.cpp @@ -70,7 +70,7 @@ void glass::DisplayEncoder(EncoderModel* model) { int chB = model->GetChannelB(); // build header label - std::string& name = GetStorage().GetString("name"); + std::string& name = GetStorage().Get("name"); char label[128]; if (!name.empty()) { wpi::format_to_n_c_str(label, sizeof(label), "{} [{},{}]###header", name, diff --git a/glass/src/lib/native/cpp/hardware/LEDDisplay.cpp b/glass/src/lib/native/cpp/hardware/LEDDisplay.cpp index 6ad7fc54a74..3ad514f5ad4 100644 --- a/glass/src/lib/native/cpp/hardware/LEDDisplay.cpp +++ b/glass/src/lib/native/cpp/hardware/LEDDisplay.cpp @@ -28,10 +28,10 @@ void glass::DisplayLEDDisplay(LEDDisplayModel* model, int index) { bool running = model->IsRunning(); auto& storage = GetStorage(); - int& numColumns = storage.GetInt("columns", 10); - bool& serpentine = storage.GetBool("serpentine", false); - int& order = storage.GetInt("order", LEDConfig::RowMajor); - int& start = storage.GetInt("start", LEDConfig::UpperLeft); + int& numColumns = storage.Get("columns", 10); + bool& serpentine = storage.Get("serpentine", false); + int& order = storage.Get("order", LEDConfig::RowMajor); + int& start = storage.Get("start", LEDConfig::UpperLeft); ImGui::PushItemWidth(ImGui::GetFontSize() * 6); ImGui::LabelText("Length", "%d", length); diff --git a/glass/src/lib/native/cpp/hardware/PWM.cpp b/glass/src/lib/native/cpp/hardware/PWM.cpp index 6f58fbaff02..a380b80dfbf 100644 --- a/glass/src/lib/native/cpp/hardware/PWM.cpp +++ b/glass/src/lib/native/cpp/hardware/PWM.cpp @@ -22,7 +22,7 @@ void glass::DisplayPWM(PWMModel* model, int index, bool outputsEnabled) { } // build label - std::string& name = GetStorage().GetString("name"); + std::string& name = GetStorage().Get("name"); char label[128]; if (!name.empty()) { wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###name", name, index); diff --git a/glass/src/lib/native/cpp/hardware/Pneumatic.cpp b/glass/src/lib/native/cpp/hardware/Pneumatic.cpp index bc39860dbb4..b290bbebaec 100644 --- a/glass/src/lib/native/cpp/hardware/Pneumatic.cpp +++ b/glass/src/lib/native/cpp/hardware/Pneumatic.cpp @@ -45,7 +45,7 @@ bool glass::DisplayPneumaticControlSolenoids(PneumaticControlModel* model, } // build header label - std::string& name = GetStorage().GetString("name"); + std::string& name = GetStorage().Get("name"); char label[128]; if (!name.empty()) { wpi::format_to_n_c_str(label, sizeof(label), "{} [{}]###header", name, diff --git a/glass/src/lib/native/cpp/hardware/Relay.cpp b/glass/src/lib/native/cpp/hardware/Relay.cpp index ff383d0fa5f..d41a76d79f2 100644 --- a/glass/src/lib/native/cpp/hardware/Relay.cpp +++ b/glass/src/lib/native/cpp/hardware/Relay.cpp @@ -34,7 +34,7 @@ void glass::DisplayRelay(RelayModel* model, int index, bool outputsEnabled) { } } - std::string& name = GetStorage().GetString("name"); + std::string& name = GetStorage().Get("name"); ImGui::PushID("name"); if (!name.empty()) { ImGui::Text("%s [%d]", name.c_str(), index); diff --git a/glass/src/lib/native/cpp/other/DeviceTree.cpp b/glass/src/lib/native/cpp/other/DeviceTree.cpp index 2360d83386a..2f79865036f 100644 --- a/glass/src/lib/native/cpp/other/DeviceTree.cpp +++ b/glass/src/lib/native/cpp/other/DeviceTree.cpp @@ -53,7 +53,7 @@ bool glass::BeginDevice(const char* id, ImGuiTreeNodeFlags flags) { PushID(id); // build label - std::string& name = GetStorage().GetString("name"); + std::string& name = GetStorage().Get("name"); char label[128]; if (name.empty()) { wpi::format_to_n_c_str(label, sizeof(label), "{}###header", id); diff --git a/glass/src/lib/native/cpp/other/Field2D.cpp b/glass/src/lib/native/cpp/other/Field2D.cpp index f8ce7155b61..901f50b775a 100644 --- a/glass/src/lib/native/cpp/other/Field2D.cpp +++ b/glass/src/lib/native/cpp/other/Field2D.cpp @@ -345,14 +345,14 @@ static bool InputPose(frc::Pose2d* pose) { } FieldInfo::FieldInfo(Storage& storage) - : m_builtin{storage.GetString("builtin", "2024 Crescendo")}, - m_filename{storage.GetString("image")}, - m_width{storage.GetFloat("width", kDefaultWidth.to())}, - m_height{storage.GetFloat("height", kDefaultHeight.to())}, - m_top{storage.GetInt("top", 0)}, - m_left{storage.GetInt("left", 0)}, - m_bottom{storage.GetInt("bottom", -1)}, - m_right{storage.GetInt("right", -1)} {} + : m_builtin{storage.Get("builtin", "2024 Crescendo")}, + m_filename{storage.Get("image")}, + m_width{storage.Get("width", kDefaultWidth.to())}, + m_height{storage.Get("height", kDefaultHeight.to())}, + m_top{storage.Get("top", 0)}, + m_left{storage.Get("left", 0)}, + m_bottom{storage.Get("bottom", -1)}, + m_right{storage.Get("right", -1)} {} void FieldInfo::DisplaySettings() { ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10); @@ -615,26 +615,26 @@ void FieldInfo::Draw(ImDrawList* drawList, const FieldFrameData& ffd) const { } ObjectInfo::ObjectInfo(Storage& storage) - : m_width{storage.GetFloat("width", - DisplayOptions::kDefaultWidth.to())}, - m_length{storage.GetFloat("length", - DisplayOptions::kDefaultLength.to())}, - m_style{storage.GetString("style"), + : m_width{storage.Get("width", + DisplayOptions::kDefaultWidth.to())}, + m_length{storage.Get("length", + DisplayOptions::kDefaultLength.to())}, + m_style{storage.Get("style"), DisplayOptions::kDefaultStyle, {"Box/Image", "Line", "Line (Closed)", "Track", "Hidden"}}, - m_weight{storage.GetFloat("weight", DisplayOptions::kDefaultWeight)}, - m_color{ - storage.GetFloatArray("color", DisplayOptions::kDefaultColorFloat)}, - m_arrows{storage.GetBool("arrows", DisplayOptions::kDefaultArrows)}, + m_weight{storage.Get("weight", DisplayOptions::kDefaultWeight)}, + m_color{storage.Get>( + "color", DisplayOptions::kDefaultColorFloat)}, + m_arrows{storage.Get("arrows", DisplayOptions::kDefaultArrows)}, m_arrowSize{ - storage.GetInt("arrowSize", DisplayOptions::kDefaultArrowSize)}, - m_arrowWeight{ - storage.GetFloat("arrowWeight", DisplayOptions::kDefaultArrowWeight)}, - m_arrowColor{storage.GetFloatArray( + storage.Get("arrowSize", DisplayOptions::kDefaultArrowSize)}, + m_arrowWeight{storage.Get("arrowWeight", + DisplayOptions::kDefaultArrowWeight)}, + m_arrowColor{storage.Get>( "arrowColor", DisplayOptions::kDefaultArrowColorFloat)}, m_selectable{ - storage.GetBool("selectable", DisplayOptions::kDefaultSelectable)}, - m_filename{storage.GetString("image")} {} + storage.Get("selectable", DisplayOptions::kDefaultSelectable)}, + m_filename{storage.Get("image")} {} DisplayOptions ObjectInfo::GetDisplayOptions() const { DisplayOptions rv{m_texture}; @@ -933,7 +933,7 @@ void glass::DisplayField2DSettings(Field2DModel* model) { field = storage.GetData(); } - EnumSetting displayUnits{GetStorage().GetString("units"), + EnumSetting displayUnits{GetStorage().Get("units"), kDisplayMeters, {"meters", "feet", "inches"}}; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); diff --git a/glass/src/lib/native/cpp/other/Mechanism2D.cpp b/glass/src/lib/native/cpp/other/Mechanism2D.cpp index b09acf81d30..9a616f3d35c 100644 --- a/glass/src/lib/native/cpp/other/Mechanism2D.cpp +++ b/glass/src/lib/native/cpp/other/Mechanism2D.cpp @@ -90,7 +90,7 @@ class BackgroundInfo { } // namespace BackgroundInfo::BackgroundInfo(Storage& storage) - : m_filename{storage.GetString("image")} {} + : m_filename{storage.Get("image")} {} void BackgroundInfo::DisplaySettings() { if (ImGui::Button("Choose image...")) { diff --git a/glass/src/lib/native/cpp/other/Plot.cpp b/glass/src/lib/native/cpp/other/Plot.cpp index 56fc04d099d..dd3095b538c 100644 --- a/glass/src/lib/native/cpp/other/Plot.cpp +++ b/glass/src/lib/native/cpp/other/Plot.cpp @@ -124,7 +124,7 @@ class Plot { const std::string& GetName() const { return m_name; } std::vector> m_series; - std::vector>& m_seriesStorage; + std::vector>& m_seriesStorage; // Returns base height; does not include actual plot height if auto-sized. int GetAutoBaseHeight(bool* isAuto, size_t i); @@ -197,26 +197,27 @@ class PlotView : public View { size_t toSeriesIndex, int yAxis = -1); PlotProvider* m_provider; - std::vector>& m_plotsStorage; + std::vector& m_plotsStorage; std::vector> m_plots; }; } // namespace PlotSeries::PlotSeries(Storage& storage) - : m_id{storage.GetString("id")}, - m_name{storage.GetString("name")}, - m_yAxis{storage.GetInt("yAxis", 0)}, - m_color{storage.GetFloatArray("color", kDefaultColor)}, - m_marker{storage.GetString("marker"), + : m_id{storage.Get("id")}, + m_name{storage.Get("name")}, + m_yAxis{storage.Get("yAxis", 0)}, + m_color{storage.Get>("color", kDefaultColor)}, + m_marker{storage.Get("marker"), 0, {"None", "Circle", "Square", "Diamond", "Up", "Down", "Left", "Right", "Cross", "Plus", "Asterisk"}}, - m_weight{storage.GetFloat("weight", IMPLOT_AUTO)}, - m_digital{ - storage.GetString("digital"), kAuto, {"Auto", "Digital", "Analog"}}, - m_digitalBitHeight{storage.GetInt("digitalBitHeight", 8)}, - m_digitalBitGap{storage.GetInt("digitalBitGap", 4)} {} + m_weight{storage.Get("weight", IMPLOT_AUTO)}, + m_digital{storage.Get("digital"), + kAuto, + {"Auto", "Digital", "Analog"}}, + m_digitalBitHeight{storage.Get("digitalBitHeight", 8)}, + m_digitalBitGap{storage.Get("digitalBitGap", 4)} {} PlotSeries::PlotSeries(Storage& storage, std::string_view id) : PlotSeries{storage} { @@ -472,44 +473,44 @@ void PlotSeries::EmitSettings(size_t i) { } Plot::PlotAxis::PlotAxis(Storage& storage, int num) - : label{storage.GetString("label")}, - min{storage.GetDouble("min", 0)}, - max{storage.GetDouble("max", 1)}, - lockMin{storage.GetBool("lockMin", false)}, - lockMax{storage.GetBool("lockMax", false)}, - autoFit{storage.GetBool("autoFit", false)}, - logScale{storage.GetBool("logScale", false)}, - invert{storage.GetBool("invert", false)}, - opposite{storage.GetBool("opposite", num != 0)}, - gridLines{storage.GetBool("gridLines", num == 0)}, - tickMarks{storage.GetBool("tickMarks", true)}, - tickLabels{storage.GetBool("tickLabels", true)} {} + : label{storage.Get("label")}, + min{storage.Get("min", 0)}, + max{storage.Get("max", 1)}, + lockMin{storage.Get("lockMin", false)}, + lockMax{storage.Get("lockMax", false)}, + autoFit{storage.Get("autoFit", false)}, + logScale{storage.Get("logScale", false)}, + invert{storage.Get("invert", false)}, + opposite{storage.Get("opposite", num != 0)}, + gridLines{storage.Get("gridLines", num == 0)}, + tickMarks{storage.Get("tickMarks", true)}, + tickLabels{storage.Get("tickLabels", true)} {} Plot::Plot(Storage& storage) : m_seriesStorage{storage.GetChildArray("series")}, - m_name{storage.GetString("name")}, - m_visible{storage.GetBool("visible", true)}, - m_backgroundColor{ - storage.GetFloatArray("backgroundColor", kDefaultBackgroundColor)}, - m_showPause{storage.GetBool("showPause", true)}, - m_lockPrevX{storage.GetBool("lockPrevX", false)}, - m_legend{storage.GetBool("legend", true)}, - m_legendOutside{storage.GetBool("legendOutside", false)}, - m_legendHorizontal{storage.GetBool("legendHorizontal", false)}, + m_name{storage.Get("name")}, + m_visible{storage.Get("visible", true)}, + m_backgroundColor{storage.Get>( + "backgroundColor", kDefaultBackgroundColor)}, + m_showPause{storage.Get("showPause", true)}, + m_lockPrevX{storage.Get("lockPrevX", false)}, + m_legend{storage.Get("legend", true)}, + m_legendOutside{storage.Get("legendOutside", false)}, + m_legendHorizontal{storage.Get("legendHorizontal", false)}, m_legendLocation{ - storage.GetInt("legendLocation", ImPlotLocation_NorthWest)}, - m_crosshairs{storage.GetBool("crosshairs", false)}, - m_mousePosition{storage.GetBool("mousePosition", true)}, - m_yAxis2{storage.GetBool("yaxis2", false)}, - m_yAxis3{storage.GetBool("yaxis3", false)}, - m_viewTime{storage.GetFloat("viewTime", 10)}, - m_autoHeight{storage.GetBool("autoHeight", true)}, - m_height{storage.GetInt("height", 300)} { + storage.Get("legendLocation", ImPlotLocation_NorthWest)}, + m_crosshairs{storage.Get("crosshairs", false)}, + m_mousePosition{storage.Get("mousePosition", true)}, + m_yAxis2{storage.Get("yaxis2", false)}, + m_yAxis3{storage.Get("yaxis3", false)}, + m_viewTime{storage.Get("viewTime", 10)}, + m_autoHeight{storage.Get("autoHeight", true)}, + m_height{storage.Get("height", 300)} { auto& axesStorage = storage.GetChildArray("axis"); axesStorage.resize(kAxisCount); for (int i = 0; i < kAxisCount; ++i) { if (!axesStorage[i]) { - axesStorage[i] = std::make_unique(); + axesStorage[i] = std::make_shared(); } m_axis.emplace_back(*axesStorage[i], i); } @@ -517,7 +518,7 @@ Plot::Plot(Storage& storage) // loop over series for (auto&& v : m_seriesStorage) { m_series.emplace_back( - std::make_unique(*v, v->ReadString("id"))); + std::make_unique(*v, v->Read("id"))); } } @@ -532,7 +533,7 @@ void Plot::DragDropAccept(PlotView& view, size_t i, int yAxis) { (yAxis == -1 || elem->GetYAxis() == yAxis); }); if (it == m_series.end()) { - m_seriesStorage.emplace_back(std::make_unique()); + m_seriesStorage.emplace_back(std::make_shared()); m_series.emplace_back(std::make_unique( *m_seriesStorage.back(), source, yAxis == -1 ? 0 : yAxis)); } @@ -792,7 +793,7 @@ PlotView::PlotView(PlotProvider* provider, Storage& storage) void PlotView::Display() { if (m_plots.empty()) { if (ImGui::Button("Add plot")) { - m_plotsStorage.emplace_back(std::make_unique()); + m_plotsStorage.emplace_back(std::make_shared()); m_plots.emplace_back(std::make_unique(*m_plotsStorage.back())); } @@ -928,7 +929,7 @@ PlotProvider::PlotProvider(Storage& storage) : WindowManager{storage} { void PlotView::Settings() { if (ImGui::Button("Add plot")) { - m_plotsStorage.emplace_back(std::make_unique()); + m_plotsStorage.emplace_back(std::make_shared()); m_plots.emplace_back(std::make_unique(*m_plotsStorage.back())); } diff --git a/glass/src/lib/native/include/glass/Storage.h b/glass/src/lib/native/include/glass/Storage.h index 765bbfe8a4d..8f7b7d072a9 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,118 +42,151 @@ class ChildIterator; */ class Storage { public: - struct Value { - enum Type { - kNone, - kInt, - kInt64, - kBool, - kFloat, - kDouble, - kString, - kChild, - kIntArray, - kInt64Array, - kBoolArray, - kFloatArray, - kDoubleArray, - kStringArray, - kChildArray - }; + using Child = std::shared_ptr; + + class Value { + public: + 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() { 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); + explicit Value(ValueData&& data) : data{std::move(data)} {} + + template + R Read(T&& defaultVal) + requires(std::convertible_to && std::assignable_from) + { + if (auto d = std::get_if(&data)) { + return *d; + } + if (auto d = Convert(&data)) { + Convert(&dataDefault); + return *d; + } + return Set(std::forward(defaultVal)); + } + + template + T& Set(Args&&... args) + requires(std::assignable_from) + { + dataDefault.emplace(args...); + return data.emplace(std::forward(args)...); + } + + ValueData data; + ValueData dataDefault; }; - using ValueMap = wpi::StringMap>; + using ValueMap = wpi::StringMap; template 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; - - 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); - void SetString(std::string_view key, std::string_view val); + template + R Read(std::string_view key, T&& defaultVal = {}) const + requires(std::convertible_to && + std::assignable_from) + { + if (auto value = FindValue(key)) { + return value->Read(std::forward(defaultVal)); + } else { + return defaultVal; + } + } + + template + void Set(std::string_view key, T&& val) + requires(std::assignable_from) + { + GetValue(key).Set(std::forward(val)); + } + + template + void Set(std::string_view key, const T& val) + requires(std::assignable_from) + { + GetValue(key).Set(val); + } + + template + void Set(std::string_view key, std::span val) + requires(std::assignable_from) + { + GetValue(key).Set(val.begin(), val.end()); + } + + void Set(std::string_view key, std::string_view val) { + GetValue(key).Set(std::string{val}); + } // 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); - std::string& GetString(std::string_view key, - std::string_view defaultVal = {}); - - 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, - std::span defaultVal = {}); - std::vector& GetStringArray( - std::string_view key, std::span defaultVal = {}); - std::vector>& GetChildArray(std::string_view key); - - Value* FindValue(std::string_view key); - Value& GetValue(std::string_view key); + template + T& Get(std::string_view key, T&& defaultVal = {}) + requires(!std::same_as && + std::assignable_from) + { + Value& value = GetValue(key); + if (auto data = std::get_if(&value.data)) { + return *data; + } + return value.Set(defaultVal); + } + + template + T& Get(std::string_view key, const T& defaultVal) + requires(!std::same_as && + std::assignable_from) + { + Value& value = GetValue(key); + if (auto data = std::get_if(&value.data)) { + return *data; + } + return value.Set(defaultVal); + } + + template + T& Get(std::string_view key, + std::span defaultVal) + requires(!std::same_as && + std::assignable_from) + { + Value& value = GetValue(key); + if (auto data = std::get_if(&value.data)) { + return *data; + } + return value.Set(defaultVal.begin(), defaultVal.end()); + } + + std::string& Get(std::string_view key, std::string_view defaultVal) { + Value& value = GetValue(key); + if (auto data = std::get_if(&value.data)) { + return *data; + } + return value.Set(defaultVal); + } + + Value* FindValue(std::string_view key) const { + auto it = m_values.find(key); + if (it == m_values.end()) { + return nullptr; + } + return &it->second; + } + + Value& GetValue(std::string_view key) { + return m_values.try_emplace(key).first->second; + } Storage& GetChild(std::string_view label_id); + std::vector& GetChildArray(std::string_view key) { + return Get>(key); + } + void SetData(std::shared_ptr&& data) { m_data = std::move(data); } template @@ -158,15 +194,23 @@ class Storage { return static_cast(m_data.get()); } + template + T& GetOrNewData(Args&&... args) { + if (!m_data) { + m_data = std::make_shared(std::forward(args)...); + } + return *static_cast(m_data.get()); + } + Storage() = default; Storage(const Storage&) = delete; Storage& operator=(const Storage&) = delete; - void Insert(std::string_view key, std::unique_ptr value) { - m_values[key] = std::move(value); + void Insert(std::string_view key, Value&& value) { + m_values.try_emplace(std::string{key}, std::move(value)); } - std::unique_ptr Erase(std::string_view key); + void Erase(std::string_view key) { m_values.erase(key); } void EraseAll() { m_values.clear(); } @@ -187,7 +231,13 @@ class Storage { * Clear settings (set to default). Calls custom clear function (if set), * otherwise calls ClearValues(). */ - void Clear(); + void Clear() { + if (m_clear) { + m_clear(); + } else { + ClearValues(); + } + } /** * Clear values (and values of children) only (set to default). Does not @@ -199,7 +249,13 @@ class Storage { * Apply settings (called after all settings have been loaded). Calls * custom apply function (if set), otherwise calls ApplyChildren(). */ - void Apply(); + void Apply() { + if (m_apply) { + m_apply(); + } else { + ApplyChildren(); + } + } /** * Apply settings to children. Does not call custom apply function. @@ -234,6 +290,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 { @@ -249,7 +360,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; } } @@ -260,7 +372,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; @@ -275,7 +388,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 diff --git a/glass/src/libnt/native/cpp/NetworkTables.cpp b/glass/src/libnt/native/cpp/NetworkTables.cpp index d661ee5f05f..2319dfc2770 100644 --- a/glass/src/libnt/native/cpp/NetworkTables.cpp +++ b/glass/src/libnt/native/cpp/NetworkTables.cpp @@ -2057,23 +2057,23 @@ void glass::DisplayNetworkTables(NetworkTablesModel* model, void NetworkTablesFlagsSettings::Update() { if (!m_pTreeView) { auto& storage = GetStorage(); - m_pTreeView = - &storage.GetBool("tree", m_defaultFlags & NetworkTablesFlags_TreeView); - m_pCombinedView = &storage.GetBool( + m_pTreeView = &storage.Get( + "tree", m_defaultFlags & NetworkTablesFlags_TreeView); + m_pCombinedView = &storage.Get( "combined", m_defaultFlags & NetworkTablesFlags_CombinedView); - m_pShowSpecial = &storage.GetBool( + m_pShowSpecial = &storage.Get( "special", m_defaultFlags & NetworkTablesFlags_ShowSpecial); - m_pShowProperties = &storage.GetBool( + m_pShowProperties = &storage.Get( "properties", m_defaultFlags & NetworkTablesFlags_ShowProperties); - m_pShowTimestamp = &storage.GetBool( + m_pShowTimestamp = &storage.Get( "timestamp", m_defaultFlags & NetworkTablesFlags_ShowTimestamp); - m_pShowServerTimestamp = &storage.GetBool( + m_pShowServerTimestamp = &storage.Get( "serverTimestamp", m_defaultFlags & NetworkTablesFlags_ShowServerTimestamp); - m_pCreateNoncanonicalKeys = &storage.GetBool( + m_pCreateNoncanonicalKeys = &storage.Get( "createNonCanonical", m_defaultFlags & NetworkTablesFlags_CreateNoncanonicalKeys); - m_pPrecision = &storage.GetInt( + m_pPrecision = &storage.Get( "precision", (m_defaultFlags & NetworkTablesFlags_Precision) >> kNetworkTablesFlags_PrecisionBitShift); } diff --git a/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp b/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp index 36e892ecaa0..a36355d8fbc 100644 --- a/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp +++ b/glass/src/libnt/native/cpp/NetworkTablesProvider.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -33,12 +34,16 @@ NetworkTablesProvider::NetworkTablesProvider(Storage& storage, for (auto&& childIt : m_storage.GetChildren()) { auto id = childIt.key(); auto typePtr = m_typeCache.FindValue(id); - if (!typePtr || typePtr->type != Storage::Value::kString) { + if (!typePtr) { + continue; + } + auto strPtr = std::get_if(&typePtr->data); + if (!strPtr) { continue; } // only handle ones where we have a builder - auto builderIt = m_typeMap.find(typePtr->stringVal); + auto builderIt = m_typeMap.find(*strPtr); if (builderIt == m_typeMap.end()) { continue; } @@ -89,10 +94,13 @@ void NetworkTablesProvider::DisplayMenu() { // Add type label to smartdashboard sendables if (wpi::starts_with(entry->name, "/SmartDashboard/")) { auto typeEntry = m_typeCache.FindValue(entry->name); - if (typeEntry) { + if (!typeEntry) { + continue; + } + if (auto typeStr = std::get_if(&typeEntry->data)) { ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(96, 96, 96, 255)); - ImGui::Text("%s", typeEntry->stringVal.c_str()); + ImGui::Text("%s", typeStr->c_str()); ImGui::PopStyleColor(); ImGui::SameLine(); ImGui::Dummy(ImVec2(10.0f, 0.0f)); @@ -166,7 +174,7 @@ void NetworkTablesProvider::Update() { GetOrCreateView(builderIt->second, nt::Topic{valueData->topic}, tableName); // cache the type - m_typeCache.SetString(tableName, valueData->value.GetString()); + m_typeCache.Set(tableName, valueData->value.GetString()); } } } diff --git a/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp b/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp index bca067f779d..2796973ab45 100644 --- a/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp +++ b/glass/src/libnt/native/cpp/NetworkTablesSettings.cpp @@ -5,6 +5,7 @@ #include "glass/networktables/NetworkTablesSettings.h" #include +#include #include #include #include @@ -94,17 +95,17 @@ void NetworkTablesSettings::Thread::Main() { NetworkTablesSettings::NetworkTablesSettings(std::string_view clientName, Storage& storage, NT_Inst inst) - : m_mode{storage.GetString("mode"), + : m_mode{storage.Get("mode"), 0, {"Disabled", "Client (NT4)", "Client (NT3)", "Server"}}, m_persistentFilename{ - storage.GetString("persistentFilename", "networktables.json")}, - m_serverTeam{storage.GetString("serverTeam")}, - m_listenAddress{storage.GetString("listenAddress")}, - m_clientName{storage.GetString("clientName", clientName)}, - m_port3{storage.GetInt("port3", NT_DEFAULT_PORT3)}, - m_port4{storage.GetInt("port4", NT_DEFAULT_PORT4)}, - m_dsClient{storage.GetBool("dsClient", true)} { + storage.Get("persistentFilename", "networktables.json")}, + m_serverTeam{storage.Get("serverTeam")}, + m_listenAddress{storage.Get("listenAddress")}, + m_clientName{storage.Get("clientName", clientName)}, + m_port3{storage.Get("port3", NT_DEFAULT_PORT3)}, + m_port4{storage.Get("port4", NT_DEFAULT_PORT4)}, + m_dsClient{storage.Get("dsClient", true)} { m_thread.Start(inst); } diff --git a/roborioteamnumbersetter/src/main/native/cpp/App.cpp b/roborioteamnumbersetter/src/main/native/cpp/App.cpp index 4394b3d841b..00bdf917ca2 100644 --- a/roborioteamnumbersetter/src/main/native/cpp/App.cpp +++ b/roborioteamnumbersetter/src/main/native/cpp/App.cpp @@ -52,7 +52,7 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); struct TeamNumberRefHolder { explicit TeamNumberRefHolder(glass::Storage& storage) - : teamNumber{storage.GetInt("TeamNumber", 0)} {} + : teamNumber{storage.Get("TeamNumber", 0)} {} int& teamNumber; }; diff --git a/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp b/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp index cb238d87e6a..1242425c32c 100644 --- a/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp +++ b/simulation/halsim_gui/src/main/native/cpp/DriverStationGui.cpp @@ -133,7 +133,7 @@ class KeyboardJoystick : public SystemJoystick { float& maxAbsValue; }; - std::vector>& m_axisStorage; + std::vector& m_axisStorage; std::vector m_axisConfig; static constexpr int kMaxButtonCount = 32; @@ -152,7 +152,7 @@ class KeyboardJoystick : public SystemJoystick { int& key315; }; - std::vector>& m_povStorage; + std::vector& m_povStorage; std::vector m_povConfig; }; @@ -485,11 +485,11 @@ void GlfwSystemJoystick::GetData(HALJoystickData* data, bool mapGamepad) const { } KeyboardJoystick::AxisConfig::AxisConfig(glass::Storage& storage) - : incKey{storage.GetInt("incKey", -1)}, - decKey{storage.GetInt("decKey", -1)}, - keyRate{storage.GetFloat("keyRate", 0.05f)}, - decayRate{storage.GetFloat("decayRate", 0.05f)}, - maxAbsValue{storage.GetFloat("maxAbsValue", 1.0f)} { + : incKey{storage.Get("incKey", -1)}, + decKey{storage.Get("decKey", -1)}, + keyRate{storage.Get("keyRate", 0.05f)}, + decayRate{storage.Get("decayRate", 0.05f)}, + maxAbsValue{storage.Get("maxAbsValue", 1.0f)} { // sanity check the key ranges if (incKey < -1 || incKey >= IM_ARRAYSIZE(ImGuiIO::KeysDown)) { incKey = -1; @@ -500,14 +500,14 @@ KeyboardJoystick::AxisConfig::AxisConfig(glass::Storage& storage) } KeyboardJoystick::PovConfig::PovConfig(glass::Storage& storage) - : key0{storage.GetInt("key0", -1)}, - key45{storage.GetInt("key45", -1)}, - key90{storage.GetInt("key90", -1)}, - key135{storage.GetInt("key135", -1)}, - key180{storage.GetInt("key180", -1)}, - key225{storage.GetInt("key225", -1)}, - key270{storage.GetInt("key270", -1)}, - key315{storage.GetInt("key315", -1)} { + : key0{storage.Get("key0", -1)}, + key45{storage.Get("key45", -1)}, + key90{storage.Get("key90", -1)}, + key135{storage.Get("key135", -1)}, + key180{storage.Get("key180", -1)}, + key225{storage.Get("key225", -1)}, + key270{storage.Get("key270", -1)}, + key315{storage.Get("key315", -1)} { // sanity check the key ranges if (key0 < -1 || key0 >= IM_ARRAYSIZE(ImGuiIO::KeysDown)) { key0 = -1; @@ -537,11 +537,11 @@ KeyboardJoystick::PovConfig::PovConfig(glass::Storage& storage) KeyboardJoystick::KeyboardJoystick(glass::Storage& storage, int index) : m_index{index}, - m_axisCount{storage.GetInt("axisCount", -1)}, - m_buttonCount{storage.GetInt("buttonCount", -1)}, - m_povCount{storage.GetInt("povCount", -1)}, + m_axisCount{storage.Get("axisCount", -1)}, + m_buttonCount{storage.Get("buttonCount", -1)}, + m_povCount{storage.Get("povCount", -1)}, m_axisStorage{storage.GetChildArray("axisConfig")}, - m_buttonKey{storage.GetIntArray("buttonKeys")}, + m_buttonKey{storage.Get>("buttonKeys")}, m_povStorage{storage.GetChildArray("povConfig")} { wpi::format_to_n_c_str(m_name, sizeof(m_name), "Keyboard {}", index); wpi::format_to_n_c_str(m_guid, sizeof(m_guid), "Keyboard{}", index); @@ -624,7 +624,7 @@ void KeyboardJoystick::SettingsDisplay() { } } while (m_axisCount > static_cast(m_axisConfig.size())) { - m_axisStorage.emplace_back(std::make_unique()); + m_axisStorage.emplace_back(std::make_shared()); m_axisConfig.emplace_back(*m_axisStorage.back()); } for (int i = 0; i < m_axisCount; ++i) { @@ -681,7 +681,7 @@ void KeyboardJoystick::SettingsDisplay() { } } while (m_povCount > static_cast(m_povConfig.size())) { - m_povStorage.emplace_back(std::make_unique()); + m_povStorage.emplace_back(std::make_shared()); m_povConfig.emplace_back(*m_povStorage.back()); } for (int i = 0; i < m_povCount; ++i) { @@ -863,7 +863,7 @@ GlfwKeyboardJoystick::GlfwKeyboardJoystick(glass::Storage& storage, int index) if (m_axisCount == -1 && m_axisStorage.empty()) { m_axisCount = 3; for (int i = 0; i < 3; ++i) { - m_axisStorage.emplace_back(std::make_unique()); + m_axisStorage.emplace_back(std::make_shared()); m_axisConfig.emplace_back(*m_axisStorage.back()); } m_axisConfig[0].incKey = GLFW_KEY_D; @@ -885,7 +885,7 @@ GlfwKeyboardJoystick::GlfwKeyboardJoystick(glass::Storage& storage, int index) } if (m_povCount == -1 && m_povStorage.empty()) { m_povCount = 1; - m_povStorage.emplace_back(std::make_unique()); + m_povStorage.emplace_back(std::make_shared()); m_povConfig.emplace_back(*m_povStorage.back()); m_povConfig[0].key0 = GLFW_KEY_KP_8; m_povConfig[0].key45 = GLFW_KEY_KP_9; @@ -900,7 +900,7 @@ GlfwKeyboardJoystick::GlfwKeyboardJoystick(glass::Storage& storage, int index) if (m_axisCount == -1 && m_axisStorage.empty()) { m_axisCount = 2; for (int i = 0; i < 2; ++i) { - m_axisStorage.emplace_back(std::make_unique()); + m_axisStorage.emplace_back(std::make_shared()); m_axisConfig.emplace_back(*m_axisStorage.back()); } m_axisConfig[0].incKey = GLFW_KEY_L; @@ -920,7 +920,7 @@ GlfwKeyboardJoystick::GlfwKeyboardJoystick(glass::Storage& storage, int index) if (m_axisCount == -1 && m_axisStorage.empty()) { m_axisCount = 2; for (int i = 0; i < 2; ++i) { - m_axisStorage.emplace_back(std::make_unique()); + m_axisStorage.emplace_back(std::make_shared()); m_axisConfig.emplace_back(*m_axisStorage.back()); } m_axisConfig[0].incKey = GLFW_KEY_RIGHT; @@ -976,9 +976,9 @@ const char* GlfwKeyboardJoystick::GetKeyName(int key) const { } RobotJoystick::RobotJoystick(glass::Storage& storage) - : name{storage.GetString("name")}, - guid{storage.GetString("guid")}, - useGamepad{storage.GetBool("useGamepad")} {} + : name{storage.Get("name")}, + guid{storage.Get("guid")}, + useGamepad{storage.Get("useGamepad")} {} void RobotJoystick::Update() { Clear(); @@ -1420,18 +1420,18 @@ void DriverStationGui::GlobalInit() { wpi::gui::AddEarlyExecute(DriverStationExecute); storageRoot.SetCustomApply([&storageRoot] { - gpDisableDS = &storageRoot.GetBool("disable", false); + gpDisableDS = &storageRoot.Get("disable", false); gpZeroDisconnectedJoysticks = - &storageRoot.GetBool("zeroDisconnectedJoysticks", true); + &storageRoot.Get("zeroDisconnectedJoysticks", true); gpUseEnableDisableHotkeys = - &storageRoot.GetBool("useEnableDisableHotkeys", false); - gpUseEstopHotkey = &storageRoot.GetBool("useEstopHotkey", false); + &storageRoot.Get("useEnableDisableHotkeys", false); + gpUseEstopHotkey = &storageRoot.Get("useEstopHotkey", false); auto& keyboardStorage = storageRoot.GetChildArray("keyboardJoysticks"); keyboardStorage.resize(4); for (int i = 0; i < 4; ++i) { if (!keyboardStorage[i]) { - keyboardStorage[i] = std::make_unique(); + keyboardStorage[i] = std::make_shared(); } gKeyboardJoysticks.emplace_back( std::make_unique(*keyboardStorage[i], i)); @@ -1441,7 +1441,7 @@ void DriverStationGui::GlobalInit() { robotJoystickStorage.resize(HAL_kMaxJoysticks); for (int i = 0; i < HAL_kMaxJoysticks; ++i) { if (!robotJoystickStorage[i]) { - robotJoystickStorage[i] = std::make_unique(); + robotJoystickStorage[i] = std::make_shared(); } gRobotJoysticks.emplace_back(*robotJoystickStorage[i]); }