diff --git a/include/aidge/utils/DynamicAttributes.hpp b/include/aidge/utils/DynamicAttributes.hpp index 4acc96cf1e291cceb1932ee97af35b8f1b56202b..ee881c94f7af75b6edb6017299115297ccb29185 100644 --- a/include/aidge/utils/DynamicAttributes.hpp +++ b/include/aidge/utils/DynamicAttributes.hpp @@ -48,27 +48,7 @@ public: */ template<class T> const T& getAttr(const std::string& name) const { - const auto dot = name.find('.'); - if (dot == name.npos) { -#ifdef PYBIND - // If attribute does not exist in C++, it might have been created or modified in Python - auto it = mAttrs.find(name); - if (it == mAttrs.end()) { - auto itPy = mAttrsPy.find(name); - if (itPy != mAttrsPy.end()) { - // Insert the attribute back in C++ - mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>()))); - } - } -#endif - - return future_std::any_cast<const T&>(mAttrs.at(name)); - } - else { - const auto ns = name.substr(0, dot); - const auto nsName = name.substr(dot + 1); - return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).getAttr<T>(nsName); - } + return future_std::any_cast<const T&>(get(name)); } template<class T> T& getAttr(const std::string& name) { @@ -83,26 +63,7 @@ public: ///\param value Attribute value template<class T> void addAttr(const std::string& name, const T& value) { - const auto dot = name.find('.'); - if (dot == name.npos) { - const auto& res = mAttrs.emplace(std::make_pair(name, future_std::any(value))); - AIDGE_ASSERT(res.second, "addAttr(): attribute \"{}\" already exists. Use setAttr() if this is expected.", name); - -#ifdef PYBIND - // We cannot handle Python object if the Python interpreter is not running - if (Py_IsInitialized()) { - // Keep a copy of the attribute in py::object that is updated everytime - const auto& resPy = mAttrsPy.emplace(std::make_pair(name, py::cast(value))); - AIDGE_ASSERT(resPy.second, "addAttr(): attribute \"{}\" already exists (added in Python). Use setAttr() if this is expected.", name); - } -#endif - } - else { - const auto ns = name.substr(0, dot); - const auto nsName = name.substr(dot + 1); - const auto& res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); - future_std::any_cast<DynamicAttributes&>(res.first->second).addAttr(nsName, value); - } + add(name, future_std::any(value)); } ///\brief Set an Attribute value, identified by its name. If it already exists, its value (and type, if different) is changed. @@ -111,28 +72,7 @@ public: ///\param value Attribute value template<class T> void setAttr(const std::string& name, const T& value) { - const auto dot = name.find('.'); - if (dot == name.npos) { - auto res = mAttrs.emplace(std::make_pair(name, future_std::any(value))); - if (!res.second) - res.first->second = future_std::any(value); - -#ifdef PYBIND - // We cannot handle Python object if the Python interpreter is not running - if (Py_IsInitialized()) { - // Keep a copy of the attribute in py::object that is updated everytime - auto resPy = mAttrsPy.emplace(std::make_pair(name, py::cast(value))); - if (!resPy.second) - resPy.first->second = std::move(py::cast(value)); - } -#endif - } - else { - const auto ns = name.substr(0, dot); - const auto nsName = name.substr(dot + 1); - auto res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); - future_std::any_cast<DynamicAttributes&>(res.first->second).setAttr<T>(nsName, value); - } + set(name, future_std::any(value)); } void delAttr(const std::string& name) { @@ -328,9 +268,84 @@ public: }; #endif + const future_std::any& get(const std::string& name) const + { + const auto dot = name.find('.'); + if (dot == name.npos) { +#ifdef PYBIND + // If attribute does not exist in C++, it might have been created or modified in Python + auto it = mAttrs.find(name); + if (it == mAttrs.end()) { + auto itPy = mAttrsPy.find(name); + if (itPy != mAttrsPy.end()) { + // Insert the attribute back in C++ + mAttrs.emplace(std::make_pair(name, future_std::any(itPy->second.cast<T>()))); + } + } +#endif + + return mAttrs.at(name); + } + else { + const auto ns = name.substr(0, dot); + const auto nsName = name.substr(dot + 1); + return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).get(nsName); + } + } + + void add(const std::string& name, const future_std::any& value) + { + const auto dot = name.find('.'); + if (dot == name.npos) { + const auto& res = mAttrs.emplace(std::make_pair(name, value)); + AIDGE_ASSERT(res.second, "addAttr(): attribute \"{}\" already exists. Use setAttr() if this is expected.", name); + +#ifdef PYBIND + // We cannot handle Python object if the Python interpreter is not running + if (Py_IsInitialized()) { + // Keep a copy of the attribute in py::object that is updated everytime + const auto& resPy = mAttrsPy.emplace(std::make_pair(name, py::cast(value))); + AIDGE_ASSERT(resPy.second, "addAttr(): attribute \"{}\" already exists (added in Python). Use setAttr() if this is expected.", name); + } +#endif + } + else { + const auto ns = name.substr(0, dot); + const auto nsName = name.substr(dot + 1); + const auto& res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); + future_std::any_cast<DynamicAttributes&>(res.first->second).add(nsName, value); + } + } + + void set(const std::string& name, const future_std::any& value) + { + const auto dot = name.find('.'); + if (dot == name.npos) { + auto res = mAttrs.emplace(std::make_pair(name, value)); + if (!res.second) + res.first->second = future_std::any(value); + +#ifdef PYBIND + // We cannot handle Python object if the Python interpreter is not running + if (Py_IsInitialized()) { + // Keep a copy of the attribute in py::object that is updated everytime + auto resPy = mAttrsPy.emplace(std::make_pair(name, py::cast(value))); + if (!resPy.second) + resPy.first->second = std::move(py::cast(value)); + } +#endif + } + else { + const auto ns = name.substr(0, dot); + const auto nsName = name.substr(dot + 1); + auto res = mAttrs.emplace(std::make_pair(ns, future_std::any(DynamicAttributes()))); + future_std::any_cast<DynamicAttributes&>(res.first->second).set(nsName, value); + } + } + virtual ~DynamicAttributes() {} - friend bool operator<(const DynamicAttributes& lhs, const DynamicAttributes& rhs); + friend bool operator<(const DynamicAttributes& lhs, const DynamicAttributes& rhs); private: #ifdef PYBIND diff --git a/src/backend/OperatorImpl.cpp b/src/backend/OperatorImpl.cpp index c13a3b70167d9e33469ebdedfaa2809f51246cf3..a04952c4b29a8f55ad3f7c624a2b196f604f4b6a 100644 --- a/src/backend/OperatorImpl.cpp +++ b/src/backend/OperatorImpl.cpp @@ -62,7 +62,10 @@ Aidge::ImplSpec Aidge::OperatorImpl::getRequiredSpec() const { } // Attributes if (!mOp.isAtomic()) { - requiredSpec.attrs.setAttr("subtype", mOp.type()); + requiredSpec.attrs.setAttr("type:!", mOp.type()); // :! mandatory qualifier + } + else { + requiredSpec.attrs.setAttr("type", mOp.type()); } return requiredSpec; } @@ -71,11 +74,12 @@ Aidge::ImplSpec Aidge::OperatorImpl::getBestMatch(ImplSpec requiredSpecs) const Log::debug("getBestMatch() for requirements: {}", requiredSpecs); const auto availableSpecs = getAvailableImplSpecs(); - std::vector<bool> matchingSpecs(availableSpecs.size(), false); + std::vector<int> matchingSpecs(availableSpecs.size(), -1); for (size_t s = 0; s < availableSpecs.size(); ++s) { auto spec = availableSpecs[s]; - bool match = true; + int match = true; + int priority = 0; // Check inputs for (size_t i = 0; i < requiredSpecs.inputs.size(); ++i) { @@ -111,19 +115,57 @@ Aidge::ImplSpec Aidge::OperatorImpl::getBestMatch(ImplSpec requiredSpecs) const // TODO // Check attributes - // TODO + for (const auto& attrName : requiredSpecs.attrs.getAttrsName()) { + std::string name = attrName; + std::string qualifier; + const auto qualifierPos = std::find_if(attrName.begin(), attrName.end(), + [](char c) { return c == ':'; }); + if (qualifierPos != attrName.begin()) { + name = attrName.substr(0, qualifierPos - attrName.begin()); + qualifier = attrName.substr(qualifierPos - attrName.begin()); + } - matchingSpecs[s] = match; + const bool mandatory = (qualifier == "!"); + if (mandatory) { + // Required attribute: + if (!spec.attrs.hasAttr(name)) { + // Missing attribute + match = false; + break; + } + else if (requiredSpecs.attrs.get(attrName) < spec.attrs.get(name) + || spec.attrs.get(name) < requiredSpecs.attrs.get(attrName)) + { + // Attribute value mismatch + match = false; + break; + } + } + else { + const int attrPriority = (!qualifier.empty()) ? std::stoi(qualifier) : 0; + + if (spec.attrs.hasAttr(name) + && !(requiredSpecs.attrs.get(attrName) < spec.attrs.get(name)) + && !(spec.attrs.get(name) < requiredSpecs.attrs.get(attrName))) + { + // Attribute value match + priority = std::max(priority, attrPriority); + } + } + } - Log::debug(" {} - {}", (match) ? "MATCH" : "MISMATCH", spec); + if (match) { + matchingSpecs[s] = priority; + } + + Log::debug(" {}:{} - {}", (match) ? "MATCH" : "MISMATCH", priority, spec); } // Return best match - // TODO: for now, returns the **first** match - for (size_t s = 0; s < availableSpecs.size(); ++s) { - if (matchingSpecs[s]) { - return availableSpecs[s]; - } + const auto bestMatch = std::max_element(matchingSpecs.begin(), matchingSpecs.end()); + if (*bestMatch >= 0) { + const auto bestSpecIdx = bestMatch - matchingSpecs.begin(); + return availableSpecs[bestSpecIdx]; } // If there is no match, return the required specs for the registrar, which