diff --git a/src/include/ZividPython/DataModelWrapper.h b/src/include/ZividPython/DataModelWrapper.h index ae47acce..bbb37fdf 100644 --- a/src/include/ZividPython/DataModelWrapper.h +++ b/src/include/ZividPython/DataModelWrapper.h @@ -145,6 +145,9 @@ namespace ZividPython static constexpr const char *value{ TypeName::value }; }; + template + void findAndWrapUninstantiatedNodesInDataModel(Dest &dest, const Node &node); + template py::class_ wrapDataModel(Dest &dest, const Target &target, const bool uninstantiatedNode = false) { @@ -172,12 +175,7 @@ namespace ZividPython if constexpr(Target::nodeType == Zivid::DataModel::NodeType::group) { - // TODO: Workaround for no API to access uninstansiated nodes. - // This generator should work on types and not instances. - if constexpr(std::is_same_v || std::is_same_v) - { - wrapDataModel(pyClass, typename Target::Acquisition{}, true); - } + findAndWrapUninstantiatedNodesInDataModel(pyClass, target); target.forEach([&](const auto &member) { wrapDataModel(pyClass, member); @@ -292,6 +290,60 @@ namespace ZividPython } return pyClass; } + + constexpr bool isDirectChild(const std::string_view parent, const std::string_view child) { + // Top-level nodes are always direct children of the root + if (parent.empty() && child.find('/') == std::string::npos) { + return true; + } + if (child.size() <= parent.size()) { + return false; + } + if (child.compare(0, parent.size(), parent) != 0) { + return false; + } + // Ensure there are no additional path components + const auto remainingPath = child.substr(parent.size()); + return remainingPath.find('/') == 0 && remainingPath.find('/', 1) == std::string::npos; + } + + static_assert(isDirectChild("", "TopLevel") == true); + static_assert(isDirectChild("", "TopLevel/WithChild") == false); + static_assert(isDirectChild("TopLevel", "TopLevel/WithChild") == true); + static_assert(isDirectChild("TopLevel", "TopLevel/WithChild/GrandChild") == false); + + template + void findAndWrapUninstantiatedNodesInDataModel(PyDest &dest, const Node &node) + { + node.forEach([&](const auto &member) { + using MemberType = std::remove_const_t>; + + if constexpr(MemberType::nodeType == Zivid::DataModel::NodeType::group) + { + findAndWrapUninstantiatedNodesInDataModel(dest, member); + } + else if constexpr(MemberType::nodeType == Zivid::DataModel::NodeType::leafDataModelList) + { + using ValueTypeContained = typename MemberType::ValueType::value_type; + + // Sanity check + static_assert(Zivid::DataModel::IsDataModelType::value); + + if constexpr(Zivid::Detail::TypeTraits::IsInTuple::value) + { + // This node is instantiated. + return; + } + + // Check via path that the contained type is a direct child of the dest + if constexpr(isDirectChild(DM::path, ValueTypeContained::path)) + { + wrapDataModel(dest, ValueTypeContained{}, true); + } + } + }); + } } // namespace Detail template