diff --git a/include/aidge/operator/AvgPooling.hpp b/include/aidge/operator/AvgPooling.hpp index 06ee4327e2f2d4df32c2decd73841bdf5f79a739..920829473d856b2a4c14fc0859abcd4c3b70277a 100644 --- a/include/aidge/operator/AvgPooling.hpp +++ b/include/aidge/operator/AvgPooling.hpp @@ -119,8 +119,8 @@ extern template class Aidge::AvgPooling_Op<4>; namespace { template <> const char *const EnumStrings<Aidge::AvgPoolingAttr>::data[] = { - "StrideDims", - "KernelDims" + "stride_dims", + "kernel_dims" }; } diff --git a/include/aidge/operator/BatchNorm.hpp b/include/aidge/operator/BatchNorm.hpp index b5b64eb428d709e804dd9f6711530b348e0be747..08d1f6a88d394e34dd6e351f500429113a52c9fa 100644 --- a/include/aidge/operator/BatchNorm.hpp +++ b/include/aidge/operator/BatchNorm.hpp @@ -111,7 +111,7 @@ extern template std::shared_ptr<Aidge::Node> Aidge::BatchNorm<4>(const DimSize_t namespace { template <> -const char *const EnumStrings<Aidge::BatchNormAttr>::data[] = { "Epsilon", "Momentum" }; +const char *const EnumStrings<Aidge::BatchNormAttr>::data[] = { "epsilon", "momentum" }; } #endif //AIDGE_CORE_OPERATOR_BATCHNORM_H_ diff --git a/include/aidge/operator/Cast.hpp b/include/aidge/operator/Cast.hpp index 6911053932afff6675be4eb2c713d8d3cd34b462..291669b7c57c14a77ffa6b40fa2aefab8d281fc7 100644 --- a/include/aidge/operator/Cast.hpp +++ b/include/aidge/operator/Cast.hpp @@ -93,7 +93,7 @@ inline std::shared_ptr<Node> Cast(const DataType targetType, const std::string& namespace { template <> -const char* const EnumStrings<Aidge::CastAttr>::data[] = { "TargetType" }; +const char* const EnumStrings<Aidge::CastAttr>::data[] = { "target_type" }; } #endif /* AIDGE_CORE_OPERATOR_CAST_H_ */ diff --git a/include/aidge/operator/Concat.hpp b/include/aidge/operator/Concat.hpp index 8341a93fe66d260ae3687170629b8759d0305a9c..ab14bf527dd9949f3bb2b6157619e58c7c7580ee 100644 --- a/include/aidge/operator/Concat.hpp +++ b/include/aidge/operator/Concat.hpp @@ -108,7 +108,7 @@ inline std::shared_ptr<Node> Concat(const IOIndex_t nbIn, const std::int32_t axi namespace { template <> const char* const EnumStrings<Aidge::ConcatAttr>::data[] = { - "Axis" + "axis" }; } diff --git a/include/aidge/operator/Conv.hpp b/include/aidge/operator/Conv.hpp index 87ff5854b310ca472994bd6b68fd6ae58d31e806..e89c94f968ab89f43e6ef2d95a40a6f557cc41c7 100644 --- a/include/aidge/operator/Conv.hpp +++ b/include/aidge/operator/Conv.hpp @@ -178,9 +178,9 @@ extern template class Aidge::Conv_Op<2>; namespace { template <> const char *const EnumStrings<Aidge::ConvAttr>::data[] = { - "StrideDims", - "DilationDims", - "KernelDims" + "stride_dims", + "dilation_dims", + "kernel_dims" }; } diff --git a/include/aidge/operator/ConvDepthWise.hpp b/include/aidge/operator/ConvDepthWise.hpp index c8a83ff7de62a61e8125eac29d61c3938115cd09..1acf240bfcdd256953cd96b92e3622a265aafa0b 100644 --- a/include/aidge/operator/ConvDepthWise.hpp +++ b/include/aidge/operator/ConvDepthWise.hpp @@ -140,8 +140,8 @@ extern template class Aidge::ConvDepthWise_Op<2>; namespace { template <> -const char *const EnumStrings<Aidge::ConvDepthWiseAttr>::data[] = {"StrideDims", "DilationDims", - "KernelDims"}; +const char *const EnumStrings<Aidge::ConvDepthWiseAttr>::data[] = {"stride_dims", "dilation_dims", + "kernel_dims"}; } #endif /* AIDGE_CORE_OPERATOR_CONVDEPTHWISE_H_ */ diff --git a/include/aidge/operator/Fold.hpp b/include/aidge/operator/Fold.hpp index 28127f9efe437531a64d228f7ed9c168edc39eb6..caf904e870425c000687ccd95397c92744020eec 100644 --- a/include/aidge/operator/Fold.hpp +++ b/include/aidge/operator/Fold.hpp @@ -133,10 +133,10 @@ extern template class Aidge::Fold_Op<2>; namespace { template <> const char *const EnumStrings<Aidge::FoldAttr>::data[] = { - "OutputDims", - "StrideDims", - "DilationDims", - "KernelDims" + "output_dims", + "stride_dims", + "dilation_dims", + "kernel_dims" }; } diff --git a/include/aidge/operator/Gather.hpp b/include/aidge/operator/Gather.hpp index 3e9b780732fa9144f2e58bef854d1b42d063d0bf..5f3917e486e2e2188bfd23bd58a13b51d5fc7a59 100644 --- a/include/aidge/operator/Gather.hpp +++ b/include/aidge/operator/Gather.hpp @@ -114,7 +114,7 @@ inline std::shared_ptr<Node> Gather(std::int8_t axis = 0, const std::vector<int6 namespace { template <> -const char *const EnumStrings<Aidge::GatherAttr>::data[] = {"Axis", "Indices", "GatheredShape"}; +const char *const EnumStrings<Aidge::GatherAttr>::data[] = {"axis", "indices", "gathered_shape"}; } #endif /* AIDGE_CORE_OPERATOR_GATHER_H_ */ diff --git a/include/aidge/operator/LeakyReLU.hpp b/include/aidge/operator/LeakyReLU.hpp index 294e7ebb009ff184c9150d2aa18067a15deeba22..3057b99f70fa3693f7e434be29dcd40fb98d4bea 100644 --- a/include/aidge/operator/LeakyReLU.hpp +++ b/include/aidge/operator/LeakyReLU.hpp @@ -99,7 +99,7 @@ inline std::shared_ptr<Node> LeakyReLU(float negativeSlope = 0.0f, const std::st namespace { template <> const char* const EnumStrings<Aidge::LeakyReLUAttr>::data[] - = {"NegativeSlope"}; + = {"negative_slope"}; } #endif /* AIDGE_CORE_OPERATOR_RELU_H_ */ diff --git a/include/aidge/operator/MaxPooling.hpp b/include/aidge/operator/MaxPooling.hpp index 082aa26bbdf1d55dcae29d1ffb2b9810db8b17d0..7e2c68681e645133812103a94e4c39ab9d1dc970 100644 --- a/include/aidge/operator/MaxPooling.hpp +++ b/include/aidge/operator/MaxPooling.hpp @@ -156,7 +156,7 @@ inline std::shared_ptr<Node> MaxPooling( namespace { template <> -const char *const EnumStrings<Aidge::MaxPoolingAttr>::data[] = {"StrideDims", "KernelDims", "CeilMode"}; +const char *const EnumStrings<Aidge::MaxPoolingAttr>::data[] = {"stride_dims", "kernel_dims", "ceil_mode"}; } #endif /* AIDGE_CORE_OPERATOR_MAXPOOLING_H_ */ diff --git a/include/aidge/operator/Memorize.hpp b/include/aidge/operator/Memorize.hpp index d6af56f2faad18b9e39c793ea68e39eac4dd2f01..bb652e833ad06df37f55d3582afd0e66cc3e97c8 100644 --- a/include/aidge/operator/Memorize.hpp +++ b/include/aidge/operator/Memorize.hpp @@ -113,9 +113,9 @@ inline std::shared_ptr<Node> Memorize(const std::uint32_t endStep, const std::st namespace { template <> const char *const EnumStrings<Aidge::MemorizeAttr>::data[] = { - "ScheduleStep", - "ForwardStep", - "EndStep" + "schedule_step", + "forward_step", + "end_step" }; } diff --git a/include/aidge/operator/Pad.hpp b/include/aidge/operator/Pad.hpp index 5fd0f93986206e6cd958a85055159783eeb8bc8f..215fafb7fee10587dec38e77685d705f7c1bb980 100644 --- a/include/aidge/operator/Pad.hpp +++ b/include/aidge/operator/Pad.hpp @@ -139,7 +139,7 @@ extern template class Aidge::Pad_Op<2>; namespace { template <> -const char *const EnumStrings<Aidge::PadAttr>::data[] = {"BeginEndBorders", "BorderType", "BorderValue"}; +const char *const EnumStrings<Aidge::PadAttr>::data[] = {"begin_end_borders", "border_type", "border_value"}; template <> const char *const EnumStrings<Aidge::PadBorderType>::data[] = {"Constant", "Edge", "Reflect", "Wrap"}; diff --git a/include/aidge/operator/Pop.hpp b/include/aidge/operator/Pop.hpp index 575d56b455940ea98571110dbaa9a83de09fef37..fb3b32eeacf2e199df88b6bd0256cf6cbdaa1065 100644 --- a/include/aidge/operator/Pop.hpp +++ b/include/aidge/operator/Pop.hpp @@ -100,7 +100,7 @@ inline std::shared_ptr<Node> Pop(const std::string& name = "") { namespace { template <> const char *const EnumStrings<Aidge::PopAttr>::data[] = { - "ForwardStep" + "forward_step" }; } diff --git a/include/aidge/operator/Producer.hpp b/include/aidge/operator/Producer.hpp index 9e3bdd1ba2f601da27dea3a6a01131a0c8191eb4..d391cc629f5cd0dbb9a6961a2cb42a9ae0b186b9 100644 --- a/include/aidge/operator/Producer.hpp +++ b/include/aidge/operator/Producer.hpp @@ -160,7 +160,7 @@ std::shared_ptr<Node> addProducer(std::shared_ptr<Node>& otherNode, const IOInde namespace { template <> const char *const EnumStrings<Aidge::ProdAttr>::data[] = { - "Constant" + "constant" }; } #endif /* AIDGE_CORE_OPERATOR_PRODUCER_H_ */ diff --git a/include/aidge/operator/ReduceMean.hpp b/include/aidge/operator/ReduceMean.hpp index 3fcf19ffd13645fb28b6efcfefaf8e347b148c89..000607c60e4e3c85671e70a941bd11f3427333dd 100644 --- a/include/aidge/operator/ReduceMean.hpp +++ b/include/aidge/operator/ReduceMean.hpp @@ -127,7 +127,7 @@ inline std::shared_ptr<Node> ReduceMean(const std::vector<std::int32_t> &axes, namespace { template <> -const char *const EnumStrings<Aidge::ReduceMeanAttr>::data[] = {"Axes", "KeepDims"}; +const char *const EnumStrings<Aidge::ReduceMeanAttr>::data[] = {"axes", "keep_dims"}; } #endif /* AIDGE_CORE_OPERATOR_REDUCEMEAN_H_ */ diff --git a/include/aidge/operator/Reshape.hpp b/include/aidge/operator/Reshape.hpp index 4ea0cca30089555ff7979f141f94e5c84f04ffa1..29a08c76c248018fff87a5f765a0b62cbd23b6b7 100644 --- a/include/aidge/operator/Reshape.hpp +++ b/include/aidge/operator/Reshape.hpp @@ -107,7 +107,7 @@ inline std::shared_ptr<Node> Reshape(const std::vector<std::int64_t>& shape = {} namespace { template <> -const char *const EnumStrings<Aidge::ReshapeAttr>::data[] = { "Shape", "AllowZero" }; +const char *const EnumStrings<Aidge::ReshapeAttr>::data[] = { "shape", "allow_zero" }; } #endif /* AIDGE_CORE_OPERATOR_RESHAPE_H_ */ diff --git a/include/aidge/operator/Scaling.hpp b/include/aidge/operator/Scaling.hpp index 7d8e11b31546cd87a8d6b2d36e2929c9ef6df7a2..0683a26f6e9d8ef462c2af4693f372b43c33a144 100644 --- a/include/aidge/operator/Scaling.hpp +++ b/include/aidge/operator/Scaling.hpp @@ -105,7 +105,7 @@ inline std::shared_ptr<Node> Scaling(float scalingFactor = 1.0f, namespace { template <> const char* const EnumStrings<Aidge::ScalingAttr>::data[] - = {"ScalingFactor", "QuantizedNbBits", "IsOutputUnsigned"}; + = {"scaling_factor", "quantized_nb_bits", "is_output_unsigned"}; } #endif /* AIDGE_CORE_OPERATOR_SCALING_H_ */ diff --git a/include/aidge/operator/Shape.hpp b/include/aidge/operator/Shape.hpp index 6d2d1b5e7c212fafa5ad6457d9e0a260e96b1c90..94f237726e79d8fe7824ff2c9b2f7640bbfc716f 100644 --- a/include/aidge/operator/Shape.hpp +++ b/include/aidge/operator/Shape.hpp @@ -105,7 +105,7 @@ inline std::shared_ptr<Node> Shape(const std::int64_t start = 0, const std::int6 namespace { template <> -const char *const EnumStrings<Aidge::ShapeAttr>::data[] = {"Start", "End"}; +const char *const EnumStrings<Aidge::ShapeAttr>::data[] = {"start", "end"}; } #endif /* AIDGE_CORE_OPERATOR_SHAPE_H_ */ diff --git a/include/aidge/operator/Slice.hpp b/include/aidge/operator/Slice.hpp index 7d425a0f3589e74b54ee0834fdc4291ea7f49bad..04a67fe98f7682737bff6df18f28d568ee33e093 100644 --- a/include/aidge/operator/Slice.hpp +++ b/include/aidge/operator/Slice.hpp @@ -115,7 +115,7 @@ inline std::shared_ptr<Node> Slice(const std::vector<std::int64_t>& starts = {}, namespace { template <> -const char *const EnumStrings<Aidge::SliceAttr>::data[] = { "Starts", "Ends", "Axes", "Steps" }; +const char *const EnumStrings<Aidge::SliceAttr>::data[] = { "starts", "ends", "axes", "steps" }; } #endif /* AIDGE_CORE_OPERATOR_RELU_H_ */ diff --git a/include/aidge/operator/Softmax.hpp b/include/aidge/operator/Softmax.hpp index 70f3a561ae5c9ba4720de8419bcd5aaf32a51e47..0b7a8e57193439872c6fcc2699b9f5e55c643961 100644 --- a/include/aidge/operator/Softmax.hpp +++ b/include/aidge/operator/Softmax.hpp @@ -92,7 +92,7 @@ inline std::shared_ptr<Node> Softmax(std::int32_t axis, const std::string& name namespace { template <> -const char *const EnumStrings<Aidge::SoftmaxAttr>::data[] = {"Axis"}; +const char *const EnumStrings<Aidge::SoftmaxAttr>::data[] = {"axis"}; } #endif /* AIDGE_CORE_OPERATOR_SOFTMAX_H_ */ diff --git a/include/aidge/operator/Split.hpp b/include/aidge/operator/Split.hpp index 42baf66e6722c6f9a0d3f40f12d4f4685fcc6980..7bdec1579c8a8f46640de5caf42c01568d208059 100644 --- a/include/aidge/operator/Split.hpp +++ b/include/aidge/operator/Split.hpp @@ -34,20 +34,24 @@ enum class SplitAttr { Axis, Split }; class Split_Op : public OperatorTensor, - public Registrable<Split_Op, std::string, std::shared_ptr<OperatorImpl>(const Split_Op &)>, - public StaticAttributes<SplitAttr, std::int8_t, std::vector<DimSize_t>> { + public Registrable<Split_Op, std::string, std::shared_ptr<OperatorImpl>(const Split_Op &)> { public: static const std::string Type; +private: + using Attributes_ = StaticAttributes<SplitAttr, std::int8_t, std::vector<DimSize_t>>; + template <SplitAttr e> using attr = typename Attributes_::template attr<e>; + const std::shared_ptr<Attributes_> mAttributes; + +public: Split_Op() = delete; - using Attributes_ = StaticAttributes<SplitAttr, std::int8_t, std::vector<DimSize_t>>; - template <SplitAttr e> using attr = typename Attributes_::template attr<e>; Split_Op( std::int8_t axis, DimSize_t nbOutputs, const std::vector<DimSize_t>& split) : OperatorTensor(Type, {InputCategory::Data, InputCategory::OptionalData}, nbOutputs), - Attributes_(attr<SplitAttr::Axis>(axis), - attr<SplitAttr::Split>(split)) + mAttributes(std::make_shared<Attributes_>( + attr<SplitAttr::Axis>(axis), + attr<SplitAttr::Split>(split))) { mImpl = std::make_shared<Split_OpImpl>(*this); } @@ -60,7 +64,7 @@ public: */ Split_Op(const Split_Op &op) : OperatorTensor(op), - Attributes_(op) + mAttributes(op.mAttributes) { if (!op.backend().empty()) { SET_IMPL_MACRO(Split_Op, *this, op.backend()); @@ -81,6 +85,10 @@ public: void setBackend(const std::string &name, DeviceIdx_t device = 0) override; + inline std::shared_ptr<Attributes> attributes() const override { return mAttributes; } + inline std::int8_t& axis() const { return mAttributes->template getAttr<SplitAttr::Axis>(); } + inline std::vector<DimSize_t>& split() const { return mAttributes->template getAttr<SplitAttr::Split>(); } + static const std::vector<std::string> getInputsName(){ return {"data_input", "split"}; } @@ -105,7 +113,7 @@ inline std::shared_ptr<Node> Split(DimSize_t nbOutput, namespace { template <> -const char *const EnumStrings<Aidge::SplitAttr>::data[] = { "Axis", "Split" }; +const char *const EnumStrings<Aidge::SplitAttr>::data[] = { "axis", "split" }; } #endif /* AIDGE_CORE_OPERATOR_SPLIT_H_ */ diff --git a/include/aidge/operator/Transpose.hpp b/include/aidge/operator/Transpose.hpp index 72096448ebf0e00d73e33bdab094ca7f0b7d0633..efd9e1792d530f45754809913a7c648d82c7985e 100644 --- a/include/aidge/operator/Transpose.hpp +++ b/include/aidge/operator/Transpose.hpp @@ -105,7 +105,7 @@ inline std::shared_ptr<Node> Transpose(const std::vector<DimSize_t> &outputDimsO namespace { template <> -const char *const EnumStrings<Aidge::TransposeAttr>::data[] = {"OutputDimsOrder"}; +const char *const EnumStrings<Aidge::TransposeAttr>::data[] = {"output_dims_order"}; } #endif /* AIDGE_CORE_OPERATOR_TRANSPOSE_H_ */ diff --git a/include/aidge/operator/Unfold.hpp b/include/aidge/operator/Unfold.hpp index 169fbb05ebeff0e5d38eb9606133d6279cc31cd8..58cbcd2d756ad44ef2ec6a38d46909a114b187c2 100644 --- a/include/aidge/operator/Unfold.hpp +++ b/include/aidge/operator/Unfold.hpp @@ -137,9 +137,9 @@ extern template class Aidge::Unfold_Op<2>; namespace { template <> const char *const EnumStrings<Aidge::UnfoldAttr>::data[] = { - "StrideDims", - "DilationDims", - "KernelDims" + "stride_dims", + "dilation_dims", + "kernel_dims" }; } diff --git a/include/aidge/utils/Attributes.hpp b/include/aidge/utils/Attributes.hpp index c1f6a8a7f704b4bd813983cb178d9e5acba5a5e1..7dce3d327d42de15dc2589788b4643742ed1a463 100644 --- a/include/aidge/utils/Attributes.hpp +++ b/include/aidge/utils/Attributes.hpp @@ -42,32 +42,6 @@ constexpr std::size_t size(T (&)[N]) { return N; } * Attributes in the binding code. */ class Attributes { -protected: - /** - * @brief Convert snake_case to PascalCase. - * @param snakeCase string to convert. - */ - static std::string snakeToPascal(const std::string& snakeCase); - - - /** - * @brief Convert PascalCase to snake_case. - * @param pascalCase string to convert. - */ - static std::string pascalToSnake(const std::string& pascalCase); - - /** - * @brief Check whether a given string is in PascalCase. - * @param str String to check. - */ - static bool isPascalCase(const std::string& str); - - /** - * @brief Check whether a given string is in snake_case. - * @param str String to check. - */ - static bool isSnakeCase(const std::string& str); - public: /** * @brief Check if the attribute exists. diff --git a/include/aidge/utils/DynamicAttributes.hpp b/include/aidge/utils/DynamicAttributes.hpp index 26a2d07667da78c9b4c37ad8700fd5b17c603a4a..cf7f048dbe5999f433277c46e4e3cb9798c43674 100644 --- a/include/aidge/utils/DynamicAttributes.hpp +++ b/include/aidge/utils/DynamicAttributes.hpp @@ -50,12 +50,11 @@ public: { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name); #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(pascalToSnake(name)); + 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>()))); @@ -67,7 +66,6 @@ public: } else { const auto ns = name.substr(0, dot); - AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace for \"{}\".", ns); const auto nsName = name.substr(dot + 1); return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).getAttr<T>(nsName); } @@ -87,22 +85,20 @@ public: { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name); const auto& res = mAttrs.emplace(std::make_pair(name, future_std::any(value))); - AIDGE_ASSERT(res.second, "attribute \"{}\" already exists", name); + 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(pascalToSnake(name), py::cast(value))); - AIDGE_ASSERT(resPy.second, "attribute \"{}\" already exists (added in Python)", name); + 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); - AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace for \"{}\".", ns); 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); @@ -117,7 +113,6 @@ public: { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name); auto res = mAttrs.emplace(std::make_pair(name, future_std::any(value))); if (!res.second) res.first->second = future_std::any(value); @@ -134,7 +129,6 @@ public: } else { const auto ns = name.substr(0, dot); - AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace for \"{}\".", ns); 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); @@ -144,15 +138,15 @@ public: void delAttr(const std::string& name) { const auto dot = name.find('.'); if (dot == name.npos) { - mAttrs.erase((isSnakeCase(name)) ? snakeToPascal(name) : name); + mAttrs.erase(name); #ifdef PYBIND - mAttrsPy.erase((isPascalCase(name)) ? pascalToSnake(name) : name); + mAttrsPy.erase(name); #endif } else { const auto ns = name.substr(0, dot); const auto nsName = name.substr(dot + 1); - future_std::any_cast<DynamicAttributes&>(mAttrs.at((isSnakeCase(ns)) ? snakeToPascal(ns) : ns)).delAttr(nsName); + future_std::any_cast<DynamicAttributes&>(mAttrs.at(ns)).delAttr(nsName); } } @@ -161,18 +155,16 @@ public: { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python for \"{}\".", name); - auto it = mAttrs.find(snakeToPascal(name)); - AIDGE_ASSERT(it == mAttrs.end(), "attribute \"{}\" already exists (added in C++)", name); + auto it = mAttrs.find(name); + AIDGE_ASSERT(it == mAttrs.end(), "add_attr(): attribute \"{}\" already exists (added in C++). Use set_attr() if this is expected.", name); const auto& res = mAttrsPy.emplace(std::make_pair(name, value)); - AIDGE_ASSERT(res.second, "attribute \"{}\" already exists", name); + AIDGE_ASSERT(res.second, "add_attr(): attribute \"{}\" already exists. Use set_attr() if this is expected.", name); } else { const auto ns = name.substr(0, dot); - AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns); const auto nsName = name.substr(dot + 1); - const auto& res = mAttrs.emplace(std::make_pair(snakeToPascal(ns), DynamicAttributes())); + const auto& res = mAttrs.emplace(std::make_pair(ns, DynamicAttributes())); future_std::any_cast<DynamicAttributes&>(res.first->second).addAttrPy(nsName, std::move(value)); } @@ -182,20 +174,17 @@ public: { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python for \"{}\".", name); auto resPy = mAttrsPy.emplace(std::make_pair(name, value)); if (!resPy.second) resPy.first->second = std::move(value); // Force getAttr() to take attribute value from mAttrsPy and update mAttrs - const std::string pascalName = snakeToPascal(name); - mAttrs.erase(pascalName); + mAttrs.erase(name); } else { const auto ns = name.substr(0, dot); - AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns); const auto nsName = name.substr(dot + 1); - const auto& res = mAttrs.emplace(std::make_pair(snakeToPascal(ns), DynamicAttributes())); + const auto& res = mAttrs.emplace(std::make_pair(ns, DynamicAttributes())); future_std::any_cast<DynamicAttributes&>(res.first->second).setAttrPy(nsName, std::move(value)); } @@ -204,14 +193,12 @@ public: py::dict dict() const override { py::dict attributes; for (const auto& elt : mAttrs) { - const std::string snakeName = pascalToSnake(elt.first); if (elt.second.type() == typeid(DynamicAttributes)) { - attributes[snakeName.c_str()] = future_std::any_cast<const DynamicAttributes&>(elt.second).dict(); + attributes[elt.first.c_str()] = future_std::any_cast<const DynamicAttributes&>(elt.second).dict(); } } for (const auto& elt : mAttrsPy) { - const std::string snakeName = pascalToSnake(elt.first); - attributes[snakeName.c_str()] = elt.second; + attributes[elt.first.c_str()] = elt.second; } return attributes; } @@ -234,9 +221,8 @@ public: bool hasAttr(const std::string& name) const override final { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes for \"{}\".", name); #ifdef PYBIND - return (mAttrs.find(name) != mAttrs.cend() || mAttrsPy.find(pascalToSnake(name)) != mAttrsPy.cend()); + return (mAttrs.find(name) != mAttrs.cend() || mAttrsPy.find(name) != mAttrsPy.cend()); #else return (mAttrs.find(name) != mAttrs.cend()); @@ -244,7 +230,6 @@ public: } else { const auto ns = name.substr(0, dot); - AIDGE_ASSERT(isPascalCase(ns), "Aidge standard requires PascalCase for C++ Attributes namespace \"{}\".", ns); const auto it = mAttrs.find(ns); if (it != mAttrs.cend()) { const auto nsName = name.substr(dot + 1); @@ -260,14 +245,12 @@ public: bool hasAttrPy(const std::string& name) const override final { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python for \"{}\".", name); // Attributes might have been created in Python, the second condition is necessary. - return (mAttrs.find(snakeToPascal(name)) != mAttrs.cend() || mAttrsPy.find(name) != mAttrsPy.cend()); + return (mAttrs.find(name) != mAttrs.cend() || mAttrsPy.find(name) != mAttrsPy.cend()); } else { const auto ns = name.substr(0, dot); - AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns); - const auto it = mAttrs.find(snakeToPascal(ns)); + const auto it = mAttrs.find(ns); if (it != mAttrs.cend()) { const auto nsName = name.substr(dot + 1); return future_std::any_cast<const DynamicAttributes&>(it->second).hasAttrPy(nsName); @@ -326,12 +309,11 @@ public: inline py::object getAttrPy(const std::string& name) const override final { const auto dot = name.find('.'); if (dot == name.npos) { - AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python for \"{}\".", name); auto itPy = mAttrsPy.find(name); if (itPy == mAttrsPy.end()) { // Attribute may be a namespace - auto it = mAttrs.find(snakeToPascal(name)); - AIDGE_ASSERT(it != mAttrs.end() && it->second.type() == typeid(DynamicAttributes), "attribute \"{}\" not found", name); + auto it = mAttrs.find(name); + AIDGE_ASSERT(it != mAttrs.end() && it->second.type() == typeid(DynamicAttributes), "get_attr(): attribute \"{}\" not found", name); return py::cast(future_std::any_cast<const DynamicAttributes&>(it->second)); } else { @@ -339,11 +321,9 @@ public: } } else { - // Namespace is stored in mAttrs, so convention should be pascal. const auto ns = name.substr(0, dot); - AIDGE_ASSERT(isSnakeCase(ns), "Aidge standard requires snake_case for Attributes namespace with Python for \"{}\".", ns); const auto nsName = name.substr(dot + 1); - return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(snakeToPascal(ns))).getAttrPy(nsName); + return future_std::any_cast<const DynamicAttributes&>(mAttrs.at(ns)).getAttrPy(nsName); } }; #endif diff --git a/include/aidge/utils/StaticAttributes.hpp b/include/aidge/utils/StaticAttributes.hpp index 8fc88ff79c50751ba7b79662fc9fc430d4ed601d..3bb41b5bb0d9c2727d95a2656a1a2d5b96ff950b 100644 --- a/include/aidge/utils/StaticAttributes.hpp +++ b/include/aidge/utils/StaticAttributes.hpp @@ -180,7 +180,6 @@ public: ////////////////////////////////////// // Runtime existance check with name bool hasAttr(const std::string& name) const override final { - AIDGE_ASSERT(isPascalCase(name), "Aidge standard requires PascalCase for C++ Attributes."); for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) { if (name == EnumStrings<ATTRS_ENUM>::data[i]) { return true; @@ -192,10 +191,8 @@ public: #ifdef PYBIND bool hasAttrPy(const std::string& name) const override final { - AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python, got '{}'.", name); - const std::string pascalName = snakeToPascal(name); for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) { - if (pascalName == EnumStrings<ATTRS_ENUM>::data[i]) { + if (name == EnumStrings<ATTRS_ENUM>::data[i]) { return true; } } @@ -234,7 +231,7 @@ public: static std::set<std::string> staticGetAttrsName() { std::set<std::string> attrsName; for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) { - attrsName.insert(pascalToSnake(std::string(EnumStrings<ATTRS_ENUM>::data[i]))); + attrsName.insert(std::string(EnumStrings<ATTRS_ENUM>::data[i])); } return attrsName; } @@ -244,10 +241,9 @@ public: if (name == "__dict__") { return py::none(); } - AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python, got '{}'.", name); - const std::string pascalName = snakeToPascal(name); + for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) { - if (pascalName == EnumStrings<ATTRS_ENUM>::data[i]) { + if (name == EnumStrings<ATTRS_ENUM>::data[i]) { // https://github.com/pybind/pybind11/blob/f3e0602802c7840992c97f4960515777cad6a5c7/include/pybind11/pytypes.h#L1119-L1138 // Normal accessor would not work as we convert the tuple to a py::object which can be anything return py::detail::accessor_policies::tuple_item::get(py::cast(mAttrs), static_cast<py::size_t>(i)); @@ -264,10 +260,8 @@ public: void setAttrPy(const std::string& name, py::object&& value) override final{ - AIDGE_ASSERT(isSnakeCase(name), "Aidge standard requires snake_case for Attributes with Python, got '{}'.", name); - const std::string pascalName = snakeToPascal(name); for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) { - if (pascalName == EnumStrings<ATTRS_ENUM>::data[i]) { + if (name == EnumStrings<ATTRS_ENUM>::data[i]) { // Cannot update attribute using reference has it would require templating // Use a dirty auto tmpAttr = py::cast(mAttrs); @@ -282,10 +276,9 @@ public: py::dict dict() const override { py::dict attributes; for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) { - const std::string snakeName = pascalToSnake(EnumStrings<ATTRS_ENUM>::data[i]); - // https://github.com/pybind/pybind11/blob/f3e0602802c7840992c97f4960515777cad6a5c7/include/pybind11/pytypes.h#L1119-L1138 - // Normal accessor would not work as we convert the tuple to a py::object which can be anything - attributes[snakeName.c_str()] = py::detail::accessor_policies::tuple_item::get(py::cast(mAttrs), static_cast<py::size_t>(i)); + // https://github.com/pybind/pybind11/blob/f3e0602802c7840992c97f4960515777cad6a5c7/include/pybind11/pytypes.h#L1119-L1138 + // Normal accessor would not work as we convert the tuple to a py::object which can be anything + attributes[EnumStrings<ATTRS_ENUM>::data[i]] = py::detail::accessor_policies::tuple_item::get(py::cast(mAttrs), static_cast<py::size_t>(i)); } return attributes; } @@ -306,8 +299,7 @@ public: // AttrDict get_a() const { // py::dict attributes_; // for (std::size_t i = 0; i < size(EnumStrings<ATTRS_ENUM>::data); ++i) { - // const std::string snakeName = pascalToSnake(std::string(EnumStrings<ATTRS_ENUM>::data[i])); - // attributes_[snakeName.c_str()] = py::detail::accessor_policies::tuple_item::get(py::cast(mAttrs), static_cast<py::size_t>(i)); + // attributes_[EnumStrings<ATTRS_ENUM>::data[i]] = py::detail::accessor_policies::tuple_item::get(py::cast(mAttrs), static_cast<py::size_t>(i)); // } // return AttrDict(attributes_); // } diff --git a/python_binding/operator/pybind_Split.cpp b/python_binding/operator/pybind_Split.cpp index 6efc123864f21bf8ea02008b29fe59f31685f17c..f63a01f9815aa59cfbad0aea36f148899f44c9ea 100644 --- a/python_binding/operator/pybind_Split.cpp +++ b/python_binding/operator/pybind_Split.cpp @@ -21,14 +21,13 @@ namespace py = pybind11; namespace Aidge { void init_Split(py::module& m) { - py::class_<Split_Op, std::shared_ptr<Split_Op>, Attributes, OperatorTensor>(m, "SplitOp", py::multiple_inheritance()) + py::class_<Split_Op, std::shared_ptr<Split_Op>, OperatorTensor>(m, "SplitOp", py::multiple_inheritance()) .def(py::init<DimSize_t, std::int8_t, std::vector<DimSize_t>&>(), py::arg("nb_outputs"), py::arg("axis"), py::arg("split")) .def_static("get_inputs_name", &Split_Op::getInputsName) - .def_static("get_outputs_name", &Split_Op::getOutputsName) - .def_static("attributes_name", &Split_Op::staticGetAttrsName); + .def_static("get_outputs_name", &Split_Op::getOutputsName); declare_registrable<Split_Op>(m, "SplitOp"); diff --git a/python_binding/utils/pybind_Attributes.cpp b/python_binding/utils/pybind_Attributes.cpp index d449feaad9a7234e6f67215737795672fc54e9ac..6989e1274e253afa828e4a941c514fc649525af6 100644 --- a/python_binding/utils/pybind_Attributes.cpp +++ b/python_binding/utils/pybind_Attributes.cpp @@ -21,12 +21,12 @@ namespace Aidge { DynamicAttributes test_DynamicAttributes_binding() { DynamicAttributes attrs; - attrs.addAttr<int>("A", 42); - attrs.addAttr<std::string>("B", "test"); - attrs.addAttr<std::vector<bool>>("C", {true, false, true}); - attrs.addAttr("Mem.A", 1); - attrs.addAttr("Mem.Data.B", 1.0f); - attrs.addAttr("Impl.C", std::string("test")); + attrs.addAttr<int>("a", 42); + attrs.addAttr<std::string>("b", "test"); + attrs.addAttr<std::vector<bool>>("c", {true, false, true}); + attrs.addAttr("mem.a", 1); + attrs.addAttr("mem.data.b", 1.0f); + attrs.addAttr("impl.c", std::string("test")); return attrs; } diff --git a/src/operator/Shape.cpp b/src/operator/Shape.cpp index 8166712e1e5fd967bb9328e95ecf8c5388636ba7..39f5e2fe09b7ac750b8ea9d48d17fc2e97013c1a 100644 --- a/src/operator/Shape.cpp +++ b/src/operator/Shape.cpp @@ -34,17 +34,17 @@ const std::string Aidge::Shape_Op::Type = "Shape"; bool Aidge::Shape_Op::forwardDims(bool /*allowDataDependency*/) { if (inputsAssociated()) { - if (mAttributes->template getAttr<std::int64_t>("Start") < 0) - mAttributes->template getAttr<std::int64_t>("Start") += static_cast<std::int64_t>(getInput(0)->nbDims()); - if (mAttributes->template getAttr<std::int64_t>("End") < 0) - mAttributes->template getAttr<std::int64_t>("End") += static_cast<std::int64_t>(getInput(0)->nbDims()); + if (this->start() < 0) + this->start() += static_cast<std::int64_t>(getInput(0)->nbDims()); + if (this->end() < 0) + this->end() += static_cast<std::int64_t>(getInput(0)->nbDims()); - const auto start = mAttributes->template getAttr<std::int64_t>("Start"); - const auto end = mAttributes->template getAttr<std::int64_t>("End"); + const auto start = this->start(); + const auto end = this->end(); const auto nbDims = static_cast<std::int64_t>(getInput(0)->nbDims()); const DimSize_t roi = end - start + 1; - AIDGE_ASSERT(start < nbDims && end < nbDims, "'Start' and 'End' must be < {}", nbDims); + AIDGE_ASSERT(start < nbDims && end < nbDims, "'start' and 'end' must be < {}", nbDims); AIDGE_ASSERT(roi> 1, "Unvalid ROI for Shape"); mOutputs[0]->resize({roi}); diff --git a/src/operator/Split.cpp b/src/operator/Split.cpp index a0cb049b19e9411daf65bbe2a10319c62b32c1b8..5e9319906a09e774d28af0578919106fb2738d04 100644 --- a/src/operator/Split.cpp +++ b/src/operator/Split.cpp @@ -28,8 +28,8 @@ void Aidge::Split_OpImpl::forward() { const Split_Op& op = dynamic_cast<const Split_Op&>(mOp); - const auto axis = op.template getAttr<std::int8_t>("Axis"); - const auto splits = op.template getAttr<std::vector<DimSize_t>>("Split"); + const auto axis = op.axis(); + const auto splits = op.split(); const auto dims = op.getInput(0)->dims(); //Compute pre/post axis strides @@ -68,7 +68,7 @@ bool Aidge::Split_Op::forwardDims(bool allowDataDependency) { if (inputsAssociated()) { // Copy optional input #1, if present, to attribute Split if (getInput(1)) { - if (!this->template getAttr<SplitAttr::Split>().empty()) { + if (!this->split().empty()) { Log::notice("Split_Op: ignoring non-empty Split attribute because input#1 takes precedence"); } @@ -78,21 +78,21 @@ bool Aidge::Split_Op::forwardDims(bool allowDataDependency) { } std::shared_ptr<Tensor> fallback; - this->template getAttr<SplitAttr::Split>().reserve(getInput(1)->size()); + this->split().reserve(getInput(1)->size()); const auto& splits = getInput(1)->refCastFrom(fallback, NativeType<DimSize_t>::type, "cpu"); std::copy_n(static_cast<DimSize_t*>(splits.getImpl()->hostPtr()), splits.size(), - std::back_inserter(this->template getAttr<SplitAttr::Split>())); + std::back_inserter(this->split())); } // Compute output dims - if (this->template getAttr<std::int8_t>("Axis") < 0) - this->template getAttr<std::int8_t>("Axis") += static_cast<std::int8_t>(getInput(0)->nbDims()); + if (this->axis() < 0) + this->axis() += static_cast<std::int8_t>(getInput(0)->nbDims()); - DimSize_t dimToSplit = getInput(0)->dims()[this->template getAttr<std::int8_t>("Axis")]; + DimSize_t dimToSplit = getInput(0)->dims()[this->axis()]; DimSize_t nbOutput = this->nbOutputs(); // Fill Split attr if empty - if(this->template getAttr<SplitAttr::Split>().empty()) { + if(this->split().empty()) { // In case the input Split is not provided, divide the dimension of Axis into equal slices AIDGE_ASSERT(dimToSplit > nbOutput, "Split_Op: Output number {} musn't be bigger than dimension {}.", nbOutput, dimToSplit); DimSize_t baseSliceSize = dimToSplit / nbOutput; @@ -100,12 +100,12 @@ bool Aidge::Split_Op::forwardDims(bool allowDataDependency) { DimSize_t remainder = dimToSplit % nbOutput; for (DimSize_t i = 0; i < static_cast<DimSize_t>(nbOutput -1); ++i) { - this->template getAttr<SplitAttr::Split>().push_back(baseSliceSize); + this->split().push_back(baseSliceSize); } - this->template getAttr<SplitAttr::Split>().push_back(baseSliceSize + remainder); + this->split().push_back(baseSliceSize + remainder); } - const auto splits = this->template getAttr<SplitAttr::Split>(); + const auto splits = this->split(); AIDGE_ASSERT(splits.size() == nbOutput, "Split_Op: number of slices {} must be equal to number of outputs {}", splits, nbOutput); DimSize_t totalSplitSize = std::accumulate(splits.cbegin(), splits.cend(), 0); AIDGE_ASSERT(totalSplitSize == dimToSplit, "Split_Op: Total chunks size {} is different from dimension size {}.", totalSplitSize, dimToSplit); @@ -113,7 +113,7 @@ bool Aidge::Split_Op::forwardDims(bool allowDataDependency) { std::vector<DimSize_t> outDims = getInput(0)->dims(); for (std::size_t i = 0; i < nbOutput; ++i) { - outDims[this->template getAttr<std::int8_t>("Axis")] = this->template getAttr<SplitAttr::Split>()[i]; + outDims[this->axis()] = this->split()[i]; mOutputs[i]->resize(outDims); } diff --git a/src/utils/Attributes.cpp b/src/utils/Attributes.cpp deleted file mode 100644 index e79db53a60a955e3502e070cda5818d3d7b6c922..0000000000000000000000000000000000000000 --- a/src/utils/Attributes.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2023 CEA-List - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - ********************************************************************************/ - -#include "aidge/utils/Attributes.hpp" - -#include <cctype> // std::isdigit, std::islower, std::isupper, std::tolower, - // std::toupper -#include <string> - -std::string Aidge::Attributes::snakeToPascal(const std::string& snakeCase) { - std::string result; - bool to_upper = true; // Start with uppercase for PascalCase - - for (char ch : snakeCase) { - if (ch == '_') { - to_upper = true; // Next character should be uppercase - } else { - if (to_upper) { - result += std::toupper(ch); - to_upper = false; // Reset flag after making a character uppercase - } else { - result += ch; - } - } - } - return result; -} - -std::string Aidge::Attributes::pascalToSnake(const std::string& pascalCase) { - std::string result; - - for (char ch : pascalCase) { - if (std::isupper(ch)) { - if (!result.empty()) { - result += '_'; - } - result += std::tolower(ch); - } else { - result += ch; - } - } - return result; -} - -bool Aidge::Attributes::isPascalCase(const std::string& str) { - if (str.empty() || !std::isupper(str[0])) { - return false; - } - - bool expectUpper = false; - for (size_t i = 1; i < str.size(); ++i) { - if (str[i] == '_') { - return false; - } - if (std::isupper(str[i])) { - if (!expectUpper) { - return false; - } - expectUpper = false; - } else if (std::islower(str[i]) || std::isdigit(str[i])) { - expectUpper = true; - } else { - return false; - } - } - return true; -} - -bool Aidge::Attributes::isSnakeCase(const std::string& str) { - if (str.empty()) { - return false; - } - - bool lastCharWasUnderscore = false; - for (char ch : str) { - if (ch == '_') { - if (lastCharWasUnderscore) { - return false; - } - lastCharWasUnderscore = true; - } else if (!std::islower(ch) && !std::isdigit(ch)) { - return false; - } else { - lastCharWasUnderscore = false; - } - } - return true; -} diff --git a/unit_tests/utils/Test_DynamicAttributes.cpp b/unit_tests/utils/Test_DynamicAttributes.cpp index c687af95d5e865328bc5f9043e65cdec510e7b22..b8a1264b3ad954e776a5ae4c47f03cd0c3fb82c9 100644 --- a/unit_tests/utils/Test_DynamicAttributes.cpp +++ b/unit_tests/utils/Test_DynamicAttributes.cpp @@ -21,42 +21,42 @@ using namespace Aidge; TEST_CASE("[core/attributes] DynamicAttributes") { SECTION("TestAttr") { DynamicAttributes attrs; - attrs.addAttr("A", 1); - attrs.addAttr("B", 1.0f); - attrs.addAttr("C", std::string("test")); - attrs.addAttr<std::vector<bool>>("D", {false, true, false}); - - REQUIRE(attrs.getAttr<int>("A") == 1); - REQUIRE(attrs.getAttr<float>("B") == 1.0f); - REQUIRE(attrs.getAttr<std::string>("C") == "test"); - REQUIRE(attrs.getAttr<std::vector<bool>>("D") == std::vector<bool>{{false, true, false}}); - - attrs.addAttr("E", DynamicAttributes()); - attrs.getAttr<DynamicAttributes>("E").addAttr("E1", 1.0f); - attrs.getAttr<DynamicAttributes>("E").addAttr("E2", std::string("test")); - - REQUIRE(attrs.getAttr<DynamicAttributes>("E").getAttr<float>("E1") == 1.0f); - REQUIRE(attrs.getAttr<DynamicAttributes>("E").getAttr<std::string>("E2") == "test"); + attrs.addAttr("a", 1); + attrs.addAttr("b", 1.0f); + attrs.addAttr("c", std::string("test")); + attrs.addAttr<std::vector<bool>>("d", {false, true, false}); + + REQUIRE(attrs.getAttr<int>("a") == 1); + REQUIRE(attrs.getAttr<float>("b") == 1.0f); + REQUIRE(attrs.getAttr<std::string>("c") == "test"); + REQUIRE(attrs.getAttr<std::vector<bool>>("d") == std::vector<bool>{{false, true, false}}); + + attrs.addAttr("e", DynamicAttributes()); + attrs.getAttr<DynamicAttributes>("e").addAttr("e1", 1.0f); + attrs.getAttr<DynamicAttributes>("e").addAttr("e2", std::string("test")); + + REQUIRE(attrs.getAttr<DynamicAttributes>("e").getAttr<float>("e1") == 1.0f); + REQUIRE(attrs.getAttr<DynamicAttributes>("e").getAttr<std::string>("e2") == "test"); } SECTION("TestAttrNS") { DynamicAttributes attrs; - attrs.addAttr("Mem.A", 1); - attrs.addAttr("Mem.Data.B", 1.0f); - attrs.addAttr("Impl.C", std::string("test")); - attrs.addAttr<std::vector<bool>>("D", {false, true, false}); - - REQUIRE(attrs.getAttr<int>("Mem.A") == 1); - REQUIRE(attrs.getAttr<float>("Mem.Data.B") == 1.0f); - REQUIRE(attrs.getAttr<std::string>("Impl.C") == "test"); - REQUIRE(attrs.getAttr<std::vector<bool>>("D") == std::vector<bool>{{false, true, false}}); - - attrs.getAttr<DynamicAttributes>("Mem.Data").addAttr("E", 2.0f); - attrs.getAttr<DynamicAttributes>("Impl").addAttr("F", std::string("test2")); - REQUIRE(attrs.getAttr<float>("Mem.Data.E") == 2.0f); - REQUIRE(attrs.getAttr<std::string>("Impl.F") == "test2"); - - REQUIRE(attrs.getAttr<DynamicAttributes>("Mem.Data").getAttr<float>("B") == 1.0f); - REQUIRE(attrs.getAttr<DynamicAttributes>("Impl").getAttr<std::string>("C") == "test"); + attrs.addAttr("mem.a", 1); + attrs.addAttr("mem.data.b", 1.0f); + attrs.addAttr("impl.c", std::string("test")); + attrs.addAttr<std::vector<bool>>("d", {false, true, false}); + + REQUIRE(attrs.getAttr<int>("mem.a") == 1); + REQUIRE(attrs.getAttr<float>("mem.data.b") == 1.0f); + REQUIRE(attrs.getAttr<std::string>("impl.c") == "test"); + REQUIRE(attrs.getAttr<std::vector<bool>>("d") == std::vector<bool>{{false, true, false}}); + + attrs.getAttr<DynamicAttributes>("mem.data").addAttr("e", 2.0f); + attrs.getAttr<DynamicAttributes>("impl").addAttr("f", std::string("test2")); + REQUIRE(attrs.getAttr<float>("mem.data.e") == 2.0f); + REQUIRE(attrs.getAttr<std::string>("impl.f") == "test2"); + + REQUIRE(attrs.getAttr<DynamicAttributes>("mem.data").getAttr<float>("b") == 1.0f); + REQUIRE(attrs.getAttr<DynamicAttributes>("impl").getAttr<std::string>("c") == "test"); } }