From 216bc26d53a07f5d35d5db1c2360ae40a9f95ca5 Mon Sep 17 00:00:00 2001 From: Daniel Nicoletti Date: Fri, 6 Dec 2024 02:43:49 -0300 Subject: [PATCH] Use new yaml parser for grc (#478) * Use new Yaml parser for grc * Remove yaml-cpp dependency * yaml: Fix common_blocks types to be loaded by YAML * yaml: Fix clang-build when loading property map * Fix qa_Settings types when building with clang Signed-off-by: Daniel Nicoletti --- CMakeLists.txt | 8 - blocks/basic/CMakeLists.txt | 26 +- .../gnuradio-4.0/basic/common_blocks.hpp | 4 +- core/CMakeLists.txt | 70 ++++- .../gnuradio-4.0/Graph_yaml_importer.hpp | 291 +++++++++--------- core/include/gnuradio-4.0/Settings.hpp | 60 ++-- core/include/gnuradio-4.0/YamlPmt.hpp | 4 + core/include/gnuradio-4.0/YamlUtils.hpp | 128 -------- core/src/CMakeLists.txt | 55 ++-- core/test/CMakeLists.txt | 1 - core/test/qa_Settings.cpp | 4 +- core/test/qa_grc.cpp | 21 +- ...-Avoid-static-reference-to-temporary.patch | 31 -- 13 files changed, 300 insertions(+), 403 deletions(-) delete mode 100644 core/include/gnuradio-4.0/YamlUtils.hpp delete mode 100644 patches/yaml-cpp/0001-Avoid-static-reference-to-temporary.patch diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c55fde4..c27a592d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,13 +250,6 @@ FetchContent_Declare( GIT_TAG v2.0.1 # latest tag as of 2023-12-18 ) -FetchContent_Declare( - yaml-cpp - GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git - GIT_TAG 0.8.0 - PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/patches/yaml-cpp/0001-Avoid-static-reference-to-temporary.patch - UPDATE_DISCONNECTED 1) - FetchContent_Declare( vir-simd GIT_REPOSITORY https://github.com/mattkretz/vir-simd.git @@ -271,7 +264,6 @@ FetchContent_MakeAvailable( fmt pmt ut - yaml-cpp vir-simd cpp-httplib) diff --git a/blocks/basic/CMakeLists.txt b/blocks/basic/CMakeLists.txt index a6e37553..6480533a 100644 --- a/blocks/basic/CMakeLists.txt +++ b/blocks/basic/CMakeLists.txt @@ -1,7 +1,23 @@ -add_library(gr-basic INTERFACE) +add_library( + gr-basic + INTERFACE + include/gnuradio-4.0/basic/clock_source.hpp + include/gnuradio-4.0/basic/common_blocks.hpp + include/gnuradio-4.0/basic/ConverterBlocks.hpp + include/gnuradio-4.0/basic/DataSink.hpp + include/gnuradio-4.0/basic/function_generator.hpp + include/gnuradio-4.0/basic/FunctionGenerator.hpp + include/gnuradio-4.0/basic/PythonBlock.hpp + include/gnuradio-4.0/basic/PythonInterpreter.hpp + include/gnuradio-4.0/basic/Selector.hpp + include/gnuradio-4.0/basic/SignalGenerator.hpp + include/gnuradio-4.0/basic/StreamToDataSet.hpp + include/gnuradio-4.0/basic/SyncBlock.hpp + include/gnuradio-4.0/basic/Trigger.hpp) target_link_libraries(gr-basic INTERFACE gnuradio-core gnuradio-algorithm) -target_include_directories(gr-basic INTERFACE $ $) +target_include_directories(gr-basic INTERFACE $ + $) -if (ENABLE_TESTING) - add_subdirectory(test) -endif () +if(ENABLE_TESTING) + add_subdirectory(test) +endif() diff --git a/blocks/basic/include/gnuradio-4.0/basic/common_blocks.hpp b/blocks/basic/include/gnuradio-4.0/basic/common_blocks.hpp index c600cc07..195198ef 100644 --- a/blocks/basic/include/gnuradio-4.0/basic/common_blocks.hpp +++ b/blocks/basic/include/gnuradio-4.0/basic/common_blocks.hpp @@ -39,7 +39,7 @@ class builtin_multiply : public gr::Block> { template class builtin_counter : public gr::Block> { public: - static std::size_t s_event_count; + static gr::Size_t s_event_count; gr::PortIn in; gr::PortOut out; @@ -53,7 +53,7 @@ class builtin_counter : public gr::Block> { }; template -std::size_t builtin_counter::s_event_count = 0; +gr::Size_t builtin_counter::s_event_count = 0; template struct MultiAdder : public gr::Block> { diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 12a750e2..1e1e06c8 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,20 +1,64 @@ -add_library(gnuradio-core INTERFACE) -target_include_directories(gnuradio-core INTERFACE $ $ $ $) -target_link_libraries(gnuradio-core INTERFACE gnuradio-options gnuradio-meta magic_enum pmtv vir yaml-cpp::yaml-cpp) +add_library( + gnuradio-core + INTERFACE + include/gnuradio-4.0/Settings.hpp + include/gnuradio-4.0/annotated.hpp + include/gnuradio-4.0/AtomicBitset.hpp + include/gnuradio-4.0/Block.hpp + include/gnuradio-4.0/BlockModel.hpp + include/gnuradio-4.0/BlockRegistry.hpp + include/gnuradio-4.0/BlockTraits.hpp + include/gnuradio-4.0/Buffer.hpp + include/gnuradio-4.0/BufferSkeleton.hpp + include/gnuradio-4.0/CircularBuffer.hpp + include/gnuradio-4.0/ClaimStrategy.hpp + include/gnuradio-4.0/DataSet.hpp + include/gnuradio-4.0/Graph_yaml_importer.hpp + include/gnuradio-4.0/Graph.hpp + include/gnuradio-4.0/HistoryBuffer.hpp + include/gnuradio-4.0/LifeCycle.hpp + include/gnuradio-4.0/Message.hpp + include/gnuradio-4.0/plugin.hpp + include/gnuradio-4.0/PluginLoader.hpp + include/gnuradio-4.0/Port.hpp + include/gnuradio-4.0/PortTraits.hpp + include/gnuradio-4.0/Profiler.hpp + include/gnuradio-4.0/reader_writer_lock.hpp + include/gnuradio-4.0/Scheduler.hpp + include/gnuradio-4.0/Sequence.hpp + include/gnuradio-4.0/Settings.hpp + include/gnuradio-4.0/Tag.hpp + include/gnuradio-4.0/TriggerMatcher.hpp + include/gnuradio-4.0/WaitStrategy.hpp + include/gnuradio-4.0/YamlPmt.hpp) +target_include_directories( + gnuradio-core + INTERFACE $ + $ + $ + $) +target_link_libraries( + gnuradio-core + INTERFACE gnuradio-options + gnuradio-meta + magic_enum + pmtv + vir) # configure a header file to pass the CMake settings to the source code -configure_file("${PROJECT_SOURCE_DIR}/cmake/config.hpp.in" "${CMAKE_CURRENT_BINARY_DIR}/include/gnuradio-4.0/config.hpp" @ONLY) -# TODO: install configure file... but not really meaningful for header only library, since compile flags are defined by the user... +configure_file("${PROJECT_SOURCE_DIR}/cmake/config.hpp.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/gnuradio-4.0/config.hpp" @ONLY) +# TODO: install configure file... but not really meaningful for header only library, since compile flags are defined by +# the user... install( - TARGETS gnuradio-core - EXPORT graphTargets - PUBLIC_HEADER DESTINATION include/ -) + TARGETS gnuradio-core + EXPORT graphTargets + PUBLIC_HEADER DESTINATION include/) add_subdirectory(src) -if (ENABLE_TESTING) - add_subdirectory(test) - add_subdirectory(benchmarks) -endif () +if(ENABLE_TESTING) + add_subdirectory(test) + add_subdirectory(benchmarks) +endif() diff --git a/core/include/gnuradio-4.0/Graph_yaml_importer.hpp b/core/include/gnuradio-4.0/Graph_yaml_importer.hpp index c0ae8fb5..6ac02041 100644 --- a/core/include/gnuradio-4.0/Graph_yaml_importer.hpp +++ b/core/include/gnuradio-4.0/Graph_yaml_importer.hpp @@ -3,53 +3,59 @@ #include -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#pragma GCC diagnostic ignored "-Wshadow" -#endif -#include -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif +#include #include "Graph.hpp" #include "PluginLoader.hpp" -#include "YamlUtils.hpp" namespace gr { -inline gr::Graph loadGrc(PluginLoader& loader, const std::string& yamlSrc) { +inline gr::Graph loadGrc(PluginLoader& loader, std::string_view yamlSrc) { Graph testGraph; std::map createdBlocks; - YAML::Node tree = YAML::Load(yamlSrc); - auto blocks = tree["blocks"]; - for (const auto& grcBlock : blocks) { - auto name = grcBlock["name"].as(); - auto id = grcBlock["id"].as(); + const auto yaml = pmtv::yaml::deserialize(yamlSrc); + if (!yaml) { + throw gr::exception(fmt::format("Could not parse yaml: {}:{}\n{}", yaml.error().message, yaml.error().line, yamlSrc)); + } + + auto blks = std::get>(yaml.value().at("blocks")); + for (const auto& blk : blks) { + auto grcBlock = std::get(blk); + + const auto name = std::get(grcBlock["name"]); + const auto id = std::get(grcBlock["id"]); + + std::string type = "double"; /// TODO: when using saveGrc template_args is not saved, this has to be implemented - auto templateArgs = grcBlock["template_args"]; + if (auto it = grcBlock.find("template_args"); it != grcBlock.end()) { + type = std::get(it->second); + } - auto currentBlock = loader.instantiate(id, templateArgs.IsDefined() ? templateArgs.as() : "double"); + auto currentBlock = loader.instantiate(id, type); if (!currentBlock) { throw fmt::format("Unable to create block of type '{}'", id); } currentBlock->setName(name); - property_map newProperties; - - auto parameters = grcBlock["parameters"]; - currentBlock->settings().loadParametersFromYAML(parameters); - auto parametersCtx = grcBlock["ctx_parameters"]; - if (parametersCtx.IsDefined()) { - for (const auto& ctxPar : parametersCtx) { - auto ctxName = ctxPar["context"].as(); - auto ctxTime = ctxPar["time"].as(); // in ns - auto ctxParameters = ctxPar["parameters"]; - - currentBlock->settings().loadParametersFromYAML(ctxParameters, SettingsCtx{ctxTime, ctxName}); + + const auto parametersPmt = grcBlock["parameters"]; + if (const auto parameters = std::get_if(¶metersPmt)) { + currentBlock->settings().loadParametersFromPropertyMap(*parameters); + } else { + currentBlock->settings().loadParametersFromPropertyMap({}); + } + + if (auto it = grcBlock.find("ctx_parameters"); it != grcBlock.end()) { + auto parametersCtx = std::get>(it->second); + for (const auto& ctxPmt : parametersCtx) { + auto ctxPar = std::get(ctxPmt); + const auto ctxName = std::get(ctxPar["context"]); + const auto ctxTime = std::get(ctxPar["time"]); // in ns + const auto ctxParameters = std::get(ctxPar["parameters"]); + + currentBlock->settings().loadParametersFromPropertyMap(ctxParameters, SettingsCtx{ctxTime, ctxName}); } } if (const auto failed = currentBlock->settings().activateContext(); failed == std::nullopt) { @@ -58,14 +64,16 @@ inline gr::Graph loadGrc(PluginLoader& loader, const std::string& yamlSrc) { createdBlocks[name] = &testGraph.addBlock(std::move(currentBlock)); } // for blocks - for (const auto& connection : tree["connections"]) { + auto connections = std::get>(yaml.value().at("connections")); + for (const auto& conn : connections) { + auto connection = std::get>(conn); if (connection.size() != 4) { throw fmt::format("Unable to parse connection ({} instead of 4 elements)", connection.size()); } auto parseBlockPort = [&](const auto& blockField, const auto& portField) { - auto blockName = blockField.template as(); - auto node = createdBlocks.find(blockName); + const auto blockName = std::get(blockField); + auto node = createdBlocks.find(blockName); if (node == createdBlocks.end()) { throw fmt::format("Unknown node '{}'", blockName); } @@ -75,138 +83,131 @@ inline gr::Graph loadGrc(PluginLoader& loader, const std::string& yamlSrc) { PortDefinition port_definition; }; - if (portField.IsSequence()) { - if (portField.size() != 2) { - throw fmt::format("Port definition has invalid length ({} instead of 2)", portField.size()); + if (const auto portFields = std::get_if>(&portField)) { + if (portFields->size() != 2) { + throw fmt::format("Port definition has invalid length ({} instead of 2)", portFields->size()); } - const auto indexStr = portField[0].template as(); - const auto subIndexStr = portField[1].template as(); - return result{node, {detail::parseIndex(indexStr), detail::parseIndex(subIndexStr)}}; + const auto index = std::get(portFields->at(0)); + const auto subIndex = std::get(portFields->at(1)); + return result{node, {static_cast(index), static_cast(subIndex)}}; + } else { - const auto indexStr = portField.template as(); - return result{node, {detail::parseIndex(indexStr)}}; + const auto index = std::get(portField); + return result{node, {static_cast(index)}}; } }; - if (connection.size() == 4) { - auto src = parseBlockPort(connection[0], connection[1]); - auto dst = parseBlockPort(connection[2], connection[3]); - testGraph.connect(*src.block_it->second, src.port_definition, *dst.block_it->second, dst.port_definition); - } else { - } + auto src = parseBlockPort(connection[0], connection[1]); + auto dst = parseBlockPort(connection[2], connection[3]); + testGraph.connect(*src.block_it->second, src.port_definition, *dst.block_it->second, dst.port_definition); } // for connections return testGraph; } inline std::string saveGrc(const gr::Graph& testGraph) { - YAML::Emitter out; - { - detail::YamlMap root(out); - - root.writeFn("blocks", [&]() { - detail::YamlSeq nodes(out); - - auto writeBlock = [&](const auto& node) { - detail::YamlMap map(out); - map.write("name", std::string(node.name())); - - const auto& fullTypeName = node.typeName(); - std::string typeName(fullTypeName.cbegin(), std::find(fullTypeName.cbegin(), fullTypeName.cend(), '<')); - map.write("id", std::move(typeName)); - - const auto& stored = node.settings().getStoredAll(); - // Helper function to write parameters - auto writeParameters = [&](const property_map& settingsMap, const property_map& metaInformation = {}) { - detail::YamlMap parameters(out); - auto writeMap = [&](const auto& localMap) { - for (const auto& [settingsKey, settingsValue] : localMap) { - std::visit([&](const T& value) { parameters.write(settingsKey, value); }, settingsValue); - } - }; - writeMap(settingsMap); - if (!metaInformation.empty()) { - writeMap(metaInformation); - } - }; - - if (stored.contains("")) { - const auto& ctxParameters = stored.at(""); - const auto& settingsMap = ctxParameters.back().second; // write only the last parameters - if (!node.metaInformation().empty() || !settingsMap.empty()) { - map.writeFn("parameters", [&]() { writeParameters(settingsMap, node.metaInformation()); }); - } - } - // write context parameters - map.writeFn("ctx_parameters", [&]() { - detail::YamlSeq ctxParamsSeq(out); + pmtv::map_t yaml; - for (const auto& [ctx, ctxParameters] : stored) { - if (ctx == "") { - continue; - } + std::vector blocks; + testGraph.forEachBlock([&](const auto& node) { + pmtv::map_t map; + map["name"] = std::string(node.name()); - for (const auto& [ctxTime, settingsMap] : ctxParameters) { - detail::YamlMap ctxParamMap(out); - - // Convert ctxTime.context to a string, regardless of its actual type - std::string contextStr = std::visit( - [](const auto& arg) -> std::string { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return arg; - } else if constexpr (std::is_arithmetic_v) { - return std::to_string(arg); - } - return ""; - }, - ctxTime.context); - - ctxParamMap.write("context", contextStr); - ctxParamMap.write("time", ctxTime.time); - ctxParamMap.writeFn("parameters", [&]() { writeParameters(settingsMap); }); - } - } - }); - }; + const auto& fullTypeName = node.typeName(); + std::string typeName(fullTypeName.cbegin(), std::find(fullTypeName.cbegin(), fullTypeName.cend(), '<')); + map.emplace("id", std::move(typeName)); - testGraph.forEachBlock(writeBlock); - }); - - root.writeFn("connections", [&]() { - detail::YamlSeq nodes(out); - - auto writePortDefinition = [&](const auto& definition) { // - std::visit(meta::overloaded( // - [&](const PortDefinition::IndexBased& _definition) { - if (_definition.subIndex != meta::invalid_index) { - detail::YamlSeq seqPort(out); - out << _definition.topLevel; - out << _definition.subIndex; - } else { - out << _definition.topLevel; - } - }, // - [&](const PortDefinition::StringBased& _definition) { out << _definition.name; }), - definition.definition); + // Helper function to write parameters + auto writeParameters = [&](const property_map& settingsMap, const property_map& metaInformation = {}) { + pmtv::map_t parameters; + auto writeMap = [&](const auto& localMap) { + for (const auto& [settingsKey, settingsValue] : localMap) { + std::visit([&](const T& value) { parameters[settingsKey] = value; }, settingsValue); + } }; + writeMap(settingsMap); + if (!metaInformation.empty()) { + writeMap(metaInformation); + } + return parameters; + }; - auto writeEdge = [&](const auto& edge) { - out << YAML::Flow; - detail::YamlSeq seq(out); - out << edge.sourceBlock().name().data(); - writePortDefinition(edge.sourcePortDefinition()); + const auto& stored = node.settings().getStoredAll(); + if (stored.contains("")) { + const auto& ctxParameters = stored.at(""); + const auto& settingsMap = ctxParameters.back().second; // write only the last parameters + if (!node.metaInformation().empty() || !settingsMap.empty()) { + map["parameters"] = writeParameters(settingsMap, node.metaInformation()); + } + } - out << edge.destinationBlock().name().data(); - writePortDefinition(edge.destinationPortDefinition()); - }; + std::vector ctxParamsSeq; + for (const auto& [ctx, ctxParameters] : stored) { + if (ctx == "") { + continue; + } - testGraph.forEachEdge(writeEdge); - }); - } + for (const auto& [ctxTime, settingsMap] : ctxParameters) { + pmtv::map_t ctxParam; + + // Convert ctxTime.context to a string, regardless of its actual type + std::string contextStr = std::visit( + [](const auto& arg) -> std::string { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return arg; + } else if constexpr (std::is_arithmetic_v) { + return std::to_string(arg); + } + return ""; + }, + ctxTime.context); + + ctxParam["context"] = contextStr; + ctxParam["time"] = ctxTime.time; + ctxParam["parameters"] = writeParameters(settingsMap); + ctxParamsSeq.emplace_back(std::move(ctxParam)); + } + } + map["ctx_parameters"] = ctxParamsSeq; + + blocks.emplace_back(std::move(map)); + }); + yaml["blocks"] = blocks; + + std::vector connections; + testGraph.forEachEdge([&](const auto& edge) { + std::vector seq; + + auto writePortDefinition = [&](const auto& definition) { // + std::visit(meta::overloaded( // + [&](const PortDefinition::IndexBased& _definition) { + if (_definition.subIndex != meta::invalid_index) { + std::vector seqPort; + + seqPort.push_back(std::int64_t(_definition.topLevel)); + seqPort.push_back(std::int64_t(_definition.subIndex)); + seq.push_back(seqPort); + } else { + seq.push_back(std::int64_t(_definition.topLevel)); + } + }, // + [&](const PortDefinition::StringBased& _definition) { seq.push_back(_definition.name); }), + definition.definition); + }; + + seq.push_back(edge.sourceBlock().name().data()); + writePortDefinition(edge.sourcePortDefinition()); + + seq.push_back(edge.destinationBlock().name().data()); + writePortDefinition(edge.destinationPortDefinition()); + + connections.emplace_back(seq); + }); + yaml["connections"] = connections; - return out.c_str(); + return pmtv::yaml::serialize(yaml); } } // namespace gr diff --git a/core/include/gnuradio-4.0/Settings.hpp b/core/include/gnuradio-4.0/Settings.hpp index 8cfc8d4d..c1e8ec0d 100644 --- a/core/include/gnuradio-4.0/Settings.hpp +++ b/core/include/gnuradio-4.0/Settings.hpp @@ -31,8 +31,6 @@ #define NO_INLINE #endif -#include "YamlUtils.hpp" - namespace gr { namespace settings { @@ -276,10 +274,10 @@ struct SettingsBase { virtual void updateActiveParameters() noexcept = 0; /** - * @brief Loads parameters from a YAML node into a property map by matching YAML keys to TBlock's writable data members. + * @brief Loads parameters from a property_map by matching pmt keys to TBlock's writable data members. * Handles type conversion and special cases, such as std::vector. */ - virtual void loadParametersFromYAML(YAML::Node& parameters, SettingsCtx ctx = {}) = 0; + virtual void loadParametersFromPropertyMap(const property_map& parameters, SettingsCtx ctx = {}) = 0; }; // struct SettingsBase @@ -847,52 +845,34 @@ class CtxSettings : public SettingsBase { } } - NO_INLINE void loadParametersFromYAML(YAML::Node& parameters, SettingsCtx ctx = {}) override { - // Once PMT supports loading and saving YAML files, this function should accept a property_map, similar to how Settings::set() operates. + NO_INLINE void loadParametersFromPropertyMap(const property_map& parameters, SettingsCtx ctx = {}) override { property_map newProperties; - if (parameters && parameters.IsMap()) { - for (const auto& kv : parameters) { - const auto& key = kv.first.template as(); - const YAML::Node& grcValue = kv.second; - bool isSet = false; - refl::for_each_data_member_index([&](auto kIdx) { - using MemberType = refl::data_member_type; - using Type = unwrap_if_wrapped_t>; - if constexpr (settings::isWritableMember()) { - const auto fieldName = refl::data_member_name.view(); - if (!isSet && fieldName == key) { -#if (defined __clang__) && (!defined __EMSCRIPTEN__) - if constexpr (std::is_same_v>) { - // gcc-stdlibc++/clang-libc++ have different implementations for std::vector, see https://en.cppreference.com/w/cpp/container/vector_bool for details - const auto& intVector = grcValue.template as>(); // need intermediary vector - std::vector boolVector(intVector.size()); - std::ranges::transform(intVector, boolVector.begin(), [](int intValue) { return static_cast(intValue); }); - newProperties[key] = boolVector; - isSet = true; - return; - } -#endif - if constexpr (!std::is_same_v) { - newProperties[key] = grcValue.template as(); - isSet = true; - } + for (const auto& [key, value] : parameters) { + bool isSet = false; + refl::for_each_data_member_index([&](auto kIdx) { + using MemberType = refl::data_member_type; + using Type = unwrap_if_wrapped_t>; + if constexpr (settings::isWritableMember()) { + const auto fieldName = refl::data_member_name.view(); + if (!isSet && fieldName == key) { + if constexpr (!std::is_same_v) { + newProperties[key] = value; + isSet = true; } } - }); + } + }); - if (!isSet) { - if (ctx.context == "") { // store meta_information only for default - if (grcValue.IsScalar()) { - _block->meta_information[key] = grcValue.template as(); - } - } + if (!isSet) { + if (ctx.context == "") { // store meta_information only for default + _block->meta_information[key] = value; } } } if (const property_map failed = set(newProperties, ctx); !failed.empty()) { - throw gr::exception(fmt::format("settings from YAML could not be loaded: {}", failed)); + throw gr::exception(fmt::format("settings from property_map could not be loaded: {}", failed)); } } diff --git a/core/include/gnuradio-4.0/YamlPmt.hpp b/core/include/gnuradio-4.0/YamlPmt.hpp index 5131e9df..0a92861e 100644 --- a/core/include/gnuradio-4.0/YamlPmt.hpp +++ b/core/include/gnuradio-4.0/YamlPmt.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include #include @@ -214,6 +216,8 @@ struct ParseContext { std::size_t lineIdx = 0; std::size_t columnIdx = 0; + std::string_view currentLine() { return lineIdx < lines.size() ? lines[lineIdx] : std::string_view{}; } + bool documentStart() const { return lineIdx == 0 && columnIdx == 0; } bool startsWith(std::string_view sv) const { diff --git a/core/include/gnuradio-4.0/YamlUtils.hpp b/core/include/gnuradio-4.0/YamlUtils.hpp deleted file mode 100644 index e70259e6..00000000 --- a/core/include/gnuradio-4.0/YamlUtils.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef GNURADIO_YAML_UTILS_H -#define GNURADIO_YAML_UTILS_H - -#include - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#pragma GCC diagnostic ignored "-Wshadow" -#endif -#include -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -namespace YAML { -// YAML custom converter for complex numbers -template -requires std::same_as || std::same_as -struct convert> { - static Node encode(const std::complex& rhs) { - Node node; - node.push_back(rhs.real()); - node.push_back(rhs.imag()); - return node; - } - - static bool decode(const Node& node, std::complex& rhs) { - if (!node.IsSequence() || node.size() != 2) { - return false; - } - rhs = std::complex(node[0].as(), node[1].as()); - return true; - } -}; -} // namespace YAML - -namespace gr { - -namespace detail { - -template -inline auto toYamlString(const T& value) { - if constexpr (std::is_same_v>) { - return value; - } else if constexpr (std::is_same_v>) { - return value ? "true" : "false"; - } else if constexpr (requires { std::to_string(value); }) { - return std::to_string(value); - } else { - return ""; - } -} - -struct YamlSeq { - YAML::Emitter& out; - - YamlSeq(YAML::Emitter& out_) : out(out_) { out << YAML::BeginSeq; } - - ~YamlSeq() { out << YAML::EndSeq; } - - template - requires std::is_invocable_v - void writeFn(const char* /*key*/, F&& fun) { - fun(); - } -}; - -struct YamlMap { - YAML::Emitter& out; - - YamlMap(YAML::Emitter& out_) : out(out_) { out << YAML::BeginMap; } - - ~YamlMap() { out << YAML::EndMap; } - - template - void write(const std::string_view& key, const std::vector& value) { - out << YAML::Key << key.data(); - YamlSeq seq(out); - for (const auto& elem : value) { - if constexpr (std::same_as> || std::same_as>) { - writeComplexValue(out, elem); - } else { - out << YAML::Value << toYamlString(elem); - } - } - } - - template - void write(const std::string_view& key, const T& value) { - out << YAML::Key << key.data(); - if constexpr (std::same_as> || std::same_as>) { - writeComplexValue(out, value); - } else { - out << YAML::Value << toYamlString(value); - } - } - - template - void writeFn(const std::string_view& key, F&& fun) { - out << YAML::Key << key.data(); - out << YAML::Value; - fun(); - } - -private: - template - requires std::same_as> || std::same_as> - void writeComplexValue(YAML::Emitter& outEmitter, const T& value) { - YamlSeq seq(outEmitter); - outEmitter << YAML::Value << toYamlString(value.real()); - outEmitter << YAML::Value << toYamlString(value.imag()); - } -}; - -inline std::size_t parseIndex(std::string_view str) { - std::size_t index{}; - auto [_, src_ec] = std::from_chars(str.begin(), str.end(), index); - if (src_ec != std::errc()) { - throw fmt::format("Unable to parse the index"); - } - return index; -} - -} // namespace detail -} // namespace gr - -#endif // include guard diff --git a/core/src/CMakeLists.txt b/core/src/CMakeLists.txt index efba2755..c02bd14f 100644 --- a/core/src/CMakeLists.txt +++ b/core/src/CMakeLists.txt @@ -1,26 +1,37 @@ -if (ENABLE_TESTS) - add_subdirectory(test) - add_subdirectory(benchmarks) -endif () +if(ENABLE_TESTS) + add_subdirectory(test) + add_subdirectory(benchmarks) +endif() -if (NOT EMSCRIPTEN) - set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) - add_library(gnuradio-plugin SHARED plugin.cpp) - target_include_directories(gnuradio-plugin PUBLIC $ $) - target_link_libraries(gnuradio-plugin PUBLIC gnuradio-core gnuradio-options pmtv fmt yaml-cpp::yaml-cpp) +if(NOT EMSCRIPTEN) + set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) + add_library(gnuradio-plugin SHARED plugin.cpp) + target_include_directories( + gnuradio-plugin PUBLIC $ + $) + target_link_libraries( + gnuradio-plugin + PUBLIC gnuradio-core + gnuradio-options + pmtv + fmt) - include(GenerateExportHeader) - generate_export_header(gnuradio-plugin) - # TODO install exported headers + include(GenerateExportHeader) + generate_export_header(gnuradio-plugin) + # TODO install exported headers - install( - TARGETS gnuradio-plugin - EXPORT graphPluginTargets - PUBLIC_HEADER DESTINATION include/ - ) -endif () + install( + TARGETS gnuradio-plugin + EXPORT graphPluginTargets + PUBLIC_HEADER DESTINATION include/) +endif() -if (ENABLE_EXAMPLES) - add_executable(main main.cpp) - target_link_libraries(main PUBLIC gnuradio-options gnuradio-core fmt::fmt magic_enum) -endif () +if(ENABLE_EXAMPLES) + add_executable(main main.cpp) + target_link_libraries( + main + PUBLIC gnuradio-options + gnuradio-core + fmt::fmt + magic_enum) +endif() diff --git a/core/test/CMakeLists.txt b/core/test/CMakeLists.txt index de72e8a1..049d67d0 100644 --- a/core/test/CMakeLists.txt +++ b/core/test/CMakeLists.txt @@ -10,7 +10,6 @@ function(setup_test_no_asan TEST_NAME) PRIVATE gnuradio-options gnuradio-core fmt - yaml-cpp::yaml-cpp fftw gr-basic gr-fileio diff --git a/core/test/qa_Settings.cpp b/core/test/qa_Settings.cpp index 82dbf929..21e4216a 100644 --- a/core/test/qa_Settings.cpp +++ b/core/test/qa_Settings.cpp @@ -1002,8 +1002,8 @@ const boost::ut::suite TransactionTests = [] { - name: source id: gr::setting_test::Source parameters: - n_samples_max: 100 - sample_rate: 123456 + n_samples_max: !!uint32 100 + sample_rate: !!float32 123456 - name: test_block id: gr::setting_test::TestBlock - name: sink diff --git a/core/test/qa_grc.cpp b/core/test/qa_grc.cpp index deef62aa..23f71927 100644 --- a/core/test/qa_grc.cpp +++ b/core/test/qa_grc.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -142,12 +143,21 @@ constexpr std::string_view testGrc = R"( - [ArraySource, [1, 1], ArraySink, [0, 1]] )"; +std::string ymlDecodeEncode(std::string_view yml) { + const auto yaml = pmtv::yaml::deserialize(yml); + if (!yaml) { + throw gr::exception(fmt::format("Could not parse yaml: {}:{} content:\n{}", yaml.error().message, yaml.error().line, yml)); + } + + return pmtv::yaml::serialize(yaml.value()); +} + const boost::ut::suite GrcTests = [] { "Basic graph loading and storing"_test = [] { try { using namespace gr; const auto context = getContext(); - const auto graphSrc = std::string(testGrc); + const auto graphSrc = ymlDecodeEncode(testGrc); auto graph = gr::loadGrc(context->loader, graphSrc); auto graphSavedSrc = gr::saveGrc(graph); expect(checkAndPrintMissingLines(graphSrc, graphSavedSrc)); @@ -167,7 +177,7 @@ const boost::ut::suite GrcTests = [] { - name: main_source id: good::fixed_source parameters: - event_count: 100 + event_count: !!uint32 100 unknown_property: 42 - name: multiplier id: good::multiply @@ -176,7 +186,7 @@ const boost::ut::suite GrcTests = [] { - name: sink id: good::cout_sink parameters: - total_count: 100 + total_count: !!uint32 100 unknown_property: 42 connections: - [main_source, 0, multiplier, 0] @@ -185,7 +195,7 @@ const boost::ut::suite GrcTests = [] { )"; const auto context = getContext(); - const auto graphSrc = std::string(pluginsTestGrc); + const auto graphSrc = ymlDecodeEncode(pluginsTestGrc); auto graph = gr::loadGrc(context->loader, graphSrc); auto graphSavedSrc = gr::saveGrc(graph); @@ -204,11 +214,10 @@ const boost::ut::suite GrcTests = [] { // Test if we get the same graph when saving it and loading the saved // data into another graph using namespace gr; - const auto graphSrc = std::string(testGrc); try { const auto context = getContext(); - auto graph1 = gr::loadGrc(context->loader, graphSrc); + auto graph1 = gr::loadGrc(context->loader, testGrc); auto graphSavedSrc = gr::saveGrc(graph1); auto graph2 = gr::loadGrc(context->loader, graphSavedSrc); expect(eq(collectBlocks(graph1), collectBlocks(graph2))); diff --git a/patches/yaml-cpp/0001-Avoid-static-reference-to-temporary.patch b/patches/yaml-cpp/0001-Avoid-static-reference-to-temporary.patch deleted file mode 100644 index 9af3ba3a..00000000 --- a/patches/yaml-cpp/0001-Avoid-static-reference-to-temporary.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 20544e648743bc783802ffc05676825b47fc1100 Mon Sep 17 00:00:00 2001 -From: Frank Osterfeld -Date: Thu, 11 Apr 2024 23:16:44 +0200 -Subject: [PATCH] Avoid static reference to temporary - -These caused issues when used in a wasm project. ---- - src/emitterutils.cpp | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/emitterutils.cpp b/src/emitterutils.cpp -index 6cdf6de..fc41011 100644 ---- a/src/emitterutils.cpp -+++ b/src/emitterutils.cpp -@@ -173,11 +173,11 @@ bool IsValidPlainScalar(const std::string& str, FlowType::value flowType, - } - - // then check until something is disallowed -- static const RegEx& disallowed_flow = -+ static const RegEx disallowed_flow = - Exp::EndScalarInFlow() | (Exp::BlankOrBreak() + Exp::Comment()) | - Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() | - Exp::Tab() | Exp::Ampersand(); -- static const RegEx& disallowed_block = -+ static const RegEx disallowed_block = - Exp::EndScalar() | (Exp::BlankOrBreak() + Exp::Comment()) | - Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() | - Exp::Tab() | Exp::Ampersand(); --- -2.34.1 -