diff --git a/include/aidge/utils/DynamicAttributes.hpp b/include/aidge/utils/DynamicAttributes.hpp index 16d5d94a16b6c8ad01cf26bb8680f79f594b2190..dc169fb86c74fb8bd6f145a252022bd9c5b885af 100644 --- a/include/aidge/utils/DynamicAttributes.hpp +++ b/include/aidge/utils/DynamicAttributes.hpp @@ -34,6 +34,12 @@ namespace py = pybind11; namespace Aidge { +// Detection idiom to check if a type T has a less-than operator +template <typename T, typename = void> +struct has_less_than_operator : std::false_type {}; + +template <typename T> +struct has_less_than_operator<T, std::void_t<decltype(std::declval<T>() < std::declval<T>())>> : std::true_type {}; ///\todo store also a fix-sized code that indicates the type ///\todo managing complex types or excluding non-trivial, non-aggregate types @@ -344,6 +350,14 @@ public: } }; + template<typename T> + static inline typename std::enable_if<!has_less_than_operator<T>::value, void>::type makeTypeConditionallyAvailable() {} + + template<typename T> + static inline typename std::enable_if<has_less_than_operator<T>::value, void>::type makeTypeConditionallyAvailable() { + mAnyUtils.emplace(typeid(T), std::unique_ptr<AnyUtils<T>>(new AnyUtils<T>())); + } + // Stores typed utils functions for each attribute type ever used static std::map<std::type_index, std::unique_ptr<AnyUtils_>> mAnyUtils; }; @@ -407,6 +421,19 @@ namespace std { return seed; } }; + + // Special case for std::array + template <typename T, std::size_t N> + struct hash<std::array<T, N>> { + std::size_t operator()(const std::array<T, N>& iterable) const { + std::size_t seed = 0; + for (const auto& v : iterable) { + // Recursively hash the value pointed by the iterator + Aidge::hash_combine(seed, std::hash<T>()(v)); + } + return seed; + } + }; } namespace future_std { diff --git a/include/aidge/utils/StaticAttributes.hpp b/include/aidge/utils/StaticAttributes.hpp index 439d2c638731b40bec0696a73b62b99e3bfddd41..9c18d3cefe466f68edde70536bca4d493f9f9b18 100644 --- a/include/aidge/utils/StaticAttributes.hpp +++ b/include/aidge/utils/StaticAttributes.hpp @@ -24,6 +24,7 @@ #endif #include "aidge/utils/Attributes.hpp" +#include "aidge/utils/DynamicAttributes.hpp" #include "aidge/utils/ErrorHandling.hpp" namespace Aidge { @@ -322,7 +323,11 @@ private: inline typename std::enable_if<I == sizeof...(Tp), void>::type appendAttr(const std::tuple<Tp...>& /*t*/, std::map<std::string, future_std::any>& /*attrs*/) const {} template<std::size_t I = 0, typename... Tp> - inline typename std::enable_if<I < sizeof...(Tp), void>::type appendAttr(const std::tuple<Tp...>& t, std::map<std::string, future_std::any>& attrs) const { + inline typename std::enable_if<I < sizeof...(Tp), void>::type appendAttr(const std::tuple<Tp...>& t, std::map<std::string, future_std::any>& attrs) const { + // Ensure that the type will be known to DynamicAttributes + using ElementType = typename std::tuple_element<I,std::tuple<Tp...>>::type; + DynamicAttributes::makeTypeConditionallyAvailable<ElementType>(); + attrs.insert(std::make_pair(EnumStrings<ATTRS_ENUM>::data[I], future_std::any(std::get<I>(t)))); appendAttr<I + 1, Tp...>(t, attrs); } diff --git a/src/operator/GenericOperator.cpp b/src/operator/GenericOperator.cpp index b24c353524be0c795d9908206a6f9550a1e59d7b..1e28cf289960dee280457cd6ea119fcc9477cf9f 100644 --- a/src/operator/GenericOperator.cpp +++ b/src/operator/GenericOperator.cpp @@ -22,7 +22,8 @@ Aidge::GenericOperator_Op::GenericOperator_Op(const std::string& type, const std::vector<Aidge::InputCategory>& inputsCategory, Aidge::IOIndex_t nbOut) - : OperatorTensor(type, inputsCategory, nbOut) + : OperatorTensor(type, inputsCategory, nbOut), + mAttributes(std::make_shared<DynamicAttributes>()) { mImpl = std::make_shared<OperatorImpl>(*this); } diff --git a/unit_tests/recipes/Test_ToGenericOp.cpp b/unit_tests/recipes/Test_ToGenericOp.cpp index 53ad86e7ce7dbe0b3eb499dc76166feff642835a..886e07f953afa26fed885d8af35b78b1fca56ae1 100644 --- a/unit_tests/recipes/Test_ToGenericOp.cpp +++ b/unit_tests/recipes/Test_ToGenericOp.cpp @@ -50,8 +50,9 @@ TEST_CASE("[graph/convert] toGenericOp", "[toGenericOp][recipies]") { // Ensure the conversion REQUIRE(newGenOp->type() == "Conv2D"); - REQUIRE(newGenOp->getOperator()->attributes() == convOp->getOperator()->attributes()); - + const auto convOpAttr = convOp->getOperator()->attributes()->getAttrs(); + const auto newGenOpAttr = (newGenOp->getOperator()->attributes()->getAttrs()); + REQUIRE((!(newGenOpAttr < convOpAttr) && !(convOpAttr < newGenOpAttr))); } SECTION("Test MetaOperator to Generic Operator") { @@ -60,14 +61,14 @@ TEST_CASE("[graph/convert] toGenericOp", "[toGenericOp][recipies]") { REQUIRE(nbFused == 1); - std::shared_ptr<Node> MetaOpNode; + std::shared_ptr<Node> metaOpNode; for (const auto& nodePtr : g->getNodes()) { if (nodePtr->type() == "ConvReLUFC") { nodePtr->setName("ConvReLUFC_0"); - MetaOpNode = nodePtr; + metaOpNode = nodePtr; // Convert to GenericOperator toGenericOp(nodePtr); } @@ -78,7 +79,9 @@ TEST_CASE("[graph/convert] toGenericOp", "[toGenericOp][recipies]") { // Ensure the conversion REQUIRE(newGenOp->type() == "ConvReLUFC"); - REQUIRE(newGenOp->getOperator()->attributes() == MetaOpNode->getOperator()->attributes()); + const auto metaOpAttr = *std::static_pointer_cast<DynamicAttributes>(metaOpNode->getOperator()->attributes()); + const auto newGenOpAttr = *std::static_pointer_cast<DynamicAttributes>(newGenOp->getOperator()->attributes()); + REQUIRE((!(newGenOpAttr < metaOpAttr) && !(metaOpAttr < newGenOpAttr))); }