diff --git a/include/aidge/operator/GenericOperator.hpp b/include/aidge/operator/GenericOperator.hpp index dab5df9a8f2d1e7d2cd680703d70e38d564c2564..12fb7e16741e9f7ad96d51b0b847b91265c3a7d2 100644 --- a/include/aidge/operator/GenericOperator.hpp +++ b/include/aidge/operator/GenericOperator.hpp @@ -62,7 +62,12 @@ class GenericOperator_Op * @return template<class T> The parameter. */ template <class T> - T getParameter(std::string const &key) const { + const T& getParameter(std::string const &key) const { + return mParams.Get<const T>(key); + } + + template <class T> + T& getParameter(std::string const &key) { return mParams.Get<T>(key); } @@ -75,8 +80,8 @@ class GenericOperator_Op /// internal buffer in a new location (previous value is still in memory at /// its previous location) template <class T> - void addParameter(std::string const &key, T const &value) { - mParams.Add<T>(key, value); + void addParameter(std::string const &key, T&& value) { + mParams.Add<T>(key, std::forward<T>(value)); } diff --git a/include/aidge/utils/Any.hpp b/include/aidge/utils/Any.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0310c38ccd855f64c8485a114962738203f03ef5 --- /dev/null +++ b/include/aidge/utils/Any.hpp @@ -0,0 +1,154 @@ + +/******************************************************************************** + * 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 + * + ********************************************************************************/ + +#ifndef AIDGE_ANY_H_ +#define AIDGE_ANY_H_ + + +#include <typeinfo> // typeid +#include <type_traits> // std::enable_if_t, std::decay_t, std::is_same, std::is_copy_constructible, std::remove_cv, std::remove_reference +#include <assert.h> +#include <new> + +class _any { +private: + /// @brief Operation to perform on the object. + enum _Op { _Op_access, _Op_get_type_info, _Op_clone, _Op_destroy }; + + union _Arg { + const std::type_info* _M_typeinfo; + _any* _M_any; + }; + + /// @brief Stored data without type information. + void* _M_data; + + /// @brief Member function to perform type-related computations on stored data. + void (*_M_manager)(_Op, const _any*, _Arg*); + +public: + /// @brief Class to centralize functions and type information in a memory efficient way. + /// @tparam Tp Decayed stored type. + template <typename Tp> + struct Manager { + static void manage(_Op which, const _any* __any, _Arg* __arg) { + auto ptr = static_cast<const Tp*>(__any->_M_data); + switch (which) + { + case _Op_get_type_info: + __arg->_M_typeinfo = &typeid(Tp); + break; + case _Op_clone: + __arg->_M_any->_M_data = new Tp(*ptr); + __arg->_M_any->_M_manager = __any->_M_manager; + break; + case _Op_destroy: + delete ptr; + break; + } + + } + static Tp* access(const _any* __any) { + return static_cast<Tp*>(__any->_M_data); + } + + // template <typename Up> + // static void create(void* data, Up&& value) { + // data = new Tp(std::forward<Up>(value)); + // } + }; + +private: + template<typename _Tp, typename _VTp = std::decay_t<_Tp>> + using _Decay_if_not_any = std::enable_if_t<!std::is_same<_VTp, _any>::value, _VTp>; + +public: + /// @brief Default constructor + _any() noexcept : _M_manager(nullptr) { } + + /// @brief Copy constructor + /// @param __other + _any(const _any& __other) + { + if (!__other._M_manager) + _M_manager = nullptr; + else + { + _Arg __arg; + __arg._M_any = this; + __other._M_manager(_Op_clone, &__other, &__arg); + } + } + + /// @brief Move constructor + /// @param __other + _any(_any&& __other) + { + if (!__other._M_manager) + _M_manager = nullptr; + else + { + _M_data = __other._M_data; + _M_manager = __other._M_manager; + const_cast<_any*>(&__other)->_M_manager = nullptr; + } + } + + /// @brief By-value constructor. + /// @tparam T Data type. + /// @tparam VT Decayed data type. + /// @param value + template<typename T, typename VT = _Decay_if_not_any<T>, std::enable_if_t<std::is_copy_constructible<VT>::value, bool> = true> + explicit _any(T&& value) + : _M_manager(&Manager<VT>::manage), + _M_data(new VT{std::forward<T>(value)}) + {} + + ~_any() + { + if(_M_manager) { + _M_manager(_Op_destroy, this, nullptr); + _M_manager = nullptr; + } + } + + /// @brief Access type id of the value currently stored + /// @return + const std::type_info& type() const + { + if (!_M_manager) + return typeid(void); + _Arg __arg; + _M_manager(_Op_get_type_info, this, &__arg); + return *__arg._M_typeinfo; + } +}; + +/// @brief Access value stored in the object converted in the template type if possible. +/// @tparam _ValueType +/// @param __any +/// @return Stored value. +template<typename _ValueType> +inline _ValueType any_cast(const _any& __any) +{ + using _Up = std::remove_cv_t<std::remove_reference_t<_ValueType>>; + assert((std::__or_<std::is_reference<_ValueType>, std::is_copy_constructible<_ValueType>>::value && "Template argument must be a reference or CopyConstructible type")); + assert((std::is_constructible<_ValueType, const _Up&>::value && "Template argument must be constructible from a const value.")); + assert(std::is_object<_Up>::value); + assert(__any.type() == typeid(_Up)); + auto __p = static_cast<_Up*>(__any._M_data); + if (__p) + return static_cast<_ValueType>(*__p); + throw std::bad_cast(); +} + +#endif /* AIDGE_ANY_H_ */ \ No newline at end of file diff --git a/include/aidge/utils/CParameter.hpp b/include/aidge/utils/CParameter.hpp index 0f4c74ab8bccb7bc134e035a5f12d31d51663e5d..7d60ed239ae58666833c4ce227aaf16542679036 100644 --- a/include/aidge/utils/CParameter.hpp +++ b/include/aidge/utils/CParameter.hpp @@ -12,23 +12,34 @@ #ifndef AIDGE_CPARAMETER_H_ #define AIDGE_CPARAMETER_H_ -#include <assert.h> #include <map> #include <vector> +#include <type_traits> +#include <typeinfo> +#include <assert.h> + +#include "aidge/utils/Any.hpp" + namespace Aidge { ///\todo store also a fix-sized code that indicates the type ///\todo managing complex types or excluding non-trivial, non-aggregate types -class CParameter -{ +class CParameter { private: - template <typename T> - struct is_vector : std::false_type {}; - - template <typename T, typename Alloc> - struct is_vector<std::vector<T, Alloc>> : std::true_type {}; - + template<typename _ValueType> + inline _ValueType& any_cast_ref(const _any& __any) + { + using _Up = std::remove_cv_t<std::remove_reference_t<_ValueType>>; + assert(((std::is_reference<_ValueType>::value || std::is_copy_constructible<_ValueType>::value) && "Template argument must be a reference or CopyConstructible type")); + assert((std::is_constructible<_ValueType, const _Up&>::value && "Template argument must be constructible from a const value.")); + assert(std::is_object<_Up>::value); + assert(__any.type() == typeid(_Up)); + if (_any::Manager<_Up>::access(&__any)) { // assess if _any object is empty + return *static_cast<_ValueType*>(_any::Manager<_Up>::access(&__any)); + } + throw std::bad_cast(); + } public: // not copyable, not movable CParameter(CParameter const &) = delete; @@ -48,15 +59,16 @@ public: * param buffer that will get invalid after the CParam death. * \note at() throws if the parameter does not exist, using find to test for parameter existance */ - template<class T> T Get(std::string const i_ParamName) const + template<class T> T& Get(const std::string i_ParamName) { - assert(m_Params.find(i_ParamName) != m_Params.end()); - assert(m_Types.find(i_ParamName) != m_Types.end()); - assert(m_Params.at(i_ParamName) <= m_OffSet); - assert(typeid(T).name() == m_Types.at(i_ParamName)); - return *reinterpret_cast<T *>(m_BeginBuffer + m_Params.at(i_ParamName)); + return any_cast_ref<T>(m_Buffer[m_Params.at(i_ParamName)]); } + // template<class T> const T& Get(const std::string i_ParamName) const + // { + // return any_cast<T>(m_Buffer[m_Params.at(i_ParamName)]); + // } + ///\brief Add a parameter value, identified by its name ///\tparam T expected parameter type ///\param i_ParamName Parameter name @@ -64,21 +76,15 @@ public: ///\todo Pass i_Value by ref if large or not trivial ///\bug If parameter already exists, its value is changed but written in the /// internal buffer in a new location (previous value is still in memory at its previous location) - template<class T> void Add(std::string const &i_ParamName, T const &i_Value) + template<class T> void Add(const std::string &i_ParamName, T&& i_Value) { - m_Buffer.resize(m_Buffer.size() + (sizeof(T) / sizeof(uint8_t))); - m_BeginBuffer = m_Buffer.data(); // Update buffer ptr in case of memory reordering - *reinterpret_cast<T *>(m_BeginBuffer + m_OffSet) - = i_Value; // Black-magic used to add anytype into the vector - m_Params[i_ParamName] = m_OffSet; // Copy pointer offset - m_OffSet += sizeof(T); // Increment offset - - m_Types[i_ParamName] = typeid(i_Value).name(); + m_Params[i_ParamName] = m_Buffer.size(); // Copy pointer offset + m_Buffer.push_back(_any(std::forward<T>(i_Value))); } std::string getParamType(std::string const &i_ParamName){ - return m_Types[i_ParamName]; + return m_Buffer[m_Params.at(i_ParamName)].type().name(); } std::vector<std::string> getParametersName(){ @@ -91,23 +97,8 @@ public: private: std::map<std::string, std::size_t> m_Params; // { Param name : offset } - ///\brief Map to check type error - /* Note : i tried this : `std::map<std::string, std::type_info const *> mTypes;` - but looks like the type_ingo object was destroyed. - I am not a hugde fan of storing a string and making string comparison. - Maybe we can use a custom enum type (or is there a standard solution ?) - */ - std::map<std::string, std::string> m_Types; - - ///\brief All parameters values concatenated in raw binary form. - std::vector<uint8_t> m_Buffer = {}; - - ///\brief Starting address of the buffer - uint8_t *m_BeginBuffer = m_Buffer.data(); - - ///\brief Offset, in number of uint8_t, of the next parameter to write - std::size_t m_OffSet = 0; - + ///\brief All raw pointers to parameters values concatenated. Use custom any class compatible with C++14. + std::vector<_any> m_Buffer = {}; }; } diff --git a/src/graph/GraphView.cpp b/src/graph/GraphView.cpp index 7cb4e1dcf33b71bec87ea883aceb8c8a3c49a5ba..1a377fb1ce49ccac5d065cf2e994a0f876713410 100644 --- a/src/graph/GraphView.cpp +++ b/src/graph/GraphView.cpp @@ -33,13 +33,10 @@ Aidge::Connector Aidge::GraphView::operator()( (void)input; // avoid unused warning } + IOIndex_t inID = 0; for (const Connector &ctor : ctors) { assert((ctor.node() != nullptr) && "Input Connector must be associated with a node"); - (void)ctors; // avoid unused warning - } - IOIndex_t inID = 0; - for (const Connector &ctor : ctors) { ctor.node()->addChild(shared_from_this(), static_cast<std::size_t>(ctor.index()), {inNode, inID++}); } @@ -197,7 +194,7 @@ void Aidge::GraphView::forwardDims() { { assert(!std::static_pointer_cast<Tensor>(nodePtr->getOperator()->getRawInput(i))->empty()); } - + } } // Compute dimensions of every node @@ -537,7 +534,7 @@ bool Aidge::GraphView::replaceWith(std::set<std::shared_ptr<Node>> newNodes) { std::shared_ptr<Node> newInputNode; std::shared_ptr<Node> previousOutputNode; std::shared_ptr<Node> newOutputNode; - + auto gNew = std::make_shared<GraphView>(); gNew->add(newNodes, false); diff --git a/unit_tests/operator/Test_GenericOperator.cpp b/unit_tests/operator/Test_GenericOperator.cpp index 886326214a4a285fb32e5909da5114d74782ee46..2208399897f586becca798eb469344af01dbab64 100644 --- a/unit_tests/operator/Test_GenericOperator.cpp +++ b/unit_tests/operator/Test_GenericOperator.cpp @@ -20,10 +20,10 @@ using namespace Aidge; TEST_CASE("[core/operators] GenericOp(add & get parameters)", "[Operator]") { SECTION("INT") { GenericOperator_Op Testop("TestOp", 1, 1, 1); - int value = 5; const char* key = "intParam"; - Testop.addParameter(key, value); - REQUIRE(Testop.getParameter<int>(key) == value); + Testop.addParameter(key, int(5)); + int registeredVal = Testop.getParameter<int>(key); + REQUIRE(registeredVal == 5); } SECTION("LONG") { GenericOperator_Op Testop("TestOp", 1, 1, 1);