/** * * @file interface_ptr.h * @brief This file provides all necessary definitions to use and implement interface maps. * @version 0.1 * @date 2022.11.14 * @author Thomas.pfleiderer@zf.com * @copyright Copyright ZF Friedrichshaven AG (c) 2022 * */ #ifndef INTERFACE_IMPL_H #define INTERFACE_IMPL_H #include #include #include #include #include // AXIVION Same Line AutosarC++19_03-A16.2.2 #ifndef DONT_LOAD_CORE_TYPES #include "../interfaces/core.h" #endif // NOLINTBEGIN(cppcoreguidelines-macro-usage) /** * @brief Software Defined Vehicle framework. */ namespace sdv { /** * @brief Internal namespace */ namespace internal { /** * @brief Helper struct for section selection during interface map processing. */ class CSectionSelector { public: /** * @brief Any processing of interfaces after this the call to this function belongs to the default section. */ void DefineDefaultSection() { m_uiSection = -1; } /** * @brief Any processing of interfaces after the call of this function, belongs to the section with the provided * section number. * @param[in] uiSection The section number the interface table entries belong to. */ void DefineSection(int uiSection) { m_uiSection = uiSection; } /** * @brief Select the section that is supported now. * @param[in] uiSection The section number that should be considered. */ void Select(int uiSection) { m_uiUseSection = uiSection; } /** * @brief Is the current section selected. * @return Returns whether the section is currently within the selection. */ bool Selected() const { return m_uiSection < 0 || m_uiSection == m_uiUseSection; } private: int m_uiUseSection = -1; ///< The number of the section to process the interface table entries for. int m_uiSection = -1; ///< The number of the section the interface table entries belong to. }; } } /** * @brief Interface map containing the supported interface definitions. * @pre The class using the interface map should derive from IInterfaceAccess. */ #define BEGIN_SDV_INTERFACE_MAP() \ /** \ * @brief Gets the requested interface \ * @param[in] idInterface The id representing the requested interface \ * @return Returns the IInterfaceAccess pointer to this if the requested interface is found, nullptr otherwise \ */ \ sdv::interface_t GetInterface(sdv::interface_id idInterface) override \ { \ if (!idInterface) return nullptr; \ ::sdv::internal::CSectionSelector selector; \ if (selector.Selected()) \ { /** * @brief Using a namespace in the interface map. */ #define SDV_INTERFACE_USE_NAMESPACE(namespace_name) using namespace namespace_name; /** * @brief Interface entry containing the interface this class is derived from. * @param ifc The interface to return. */ #define SDV_INTERFACE_ENTRY(ifc) \ if (idInterface == sdv::GetInterfaceId()) \ return static_cast(this); /** * @brief Interface entry containing the interface a member class is derived from. * @param ifc The interface to return. The member must derive from this interface. * @param member_or_pointer The interface of the member to return. */ #define SDV_INTERFACE_ENTRY_MEMBER(ifc, member_or_pointer) \ { \ sdv::interface_t tifc = sdv::SInterfaceMemberHelper::InterfaceMapEntryHelper(member_or_pointer, idInterface); \ if (tifc) \ return tifc; \ } /** * @brief Interface entry containing the interface this class is derived from. The indirect implementation solves * ambiguous casts when the class is derived from the same interface more than once. * @param ifc The interface to return. * @param ifc_cast The interface to use for casting before casting to the interface. This is usually one of the * interfaces/classes derived from the interface */ #define SDV_INTERFACE_ENTRY_INDIRECT(ifc, ifc_cast) \ if (idInterface == sdv::GetInterfaceId()) \ return static_cast(static_cast(this)); /** * @brief Interface entry containing the interface this class is definitely not supporting (used to overload an entry * in an interface map in a base class). * @attention When the interface map was chained, this will end the current processing for the chained map, but the * calling map will still continue the processing. * @param ifc The interface to deny. */ #define SDV_INTERFACE_DENY_ENTRY(ifc) \ if (idInterface == sdv::GetInterfaceId()) \ return nullptr; /** * @brief Chain the interface map of a base class. * @param base_class The class implementing the GetInterface function. */ #define SDV_INTERFACE_CHAIN_BASE(base_class) \ { \ sdv::interface_t ifc = base_class::GetInterface(idInterface); \ if (ifc) \ return ifc; \ } /** * @brief Chain the interface map of a member as reference or as pointer. * @param member_or_pointer The member implementing the GetInterface function. */ #define SDV_INTERFACE_CHAIN_MEMBER(member_or_pointer) \ { \ sdv::interface_t ifc = sdv::InterfaceMapChainHelper(member_or_pointer, idInterface); \ if (ifc) \ return ifc; \ } /** * @brief Conditional check; when true continue the checking for interfaces. * @param condition The condition to be checked. When 'false' processing will be stopped; otherwise processing * continues. Example: @code SDV_INTERFACE_CHECK_CONDITION(CheckFunc()) @endcode */ #define SDV_INTERFACE_CHECK_CONDITION(condition) \ if (!(condition)) \ return nullptr; /** * @brief Use the condition, to select a section to process. * @param condition Condition to be checked. When 'true' processing will be limited to the section. * Example: @code SDV_INTERFACE_PROCESS_SECTION(CheckFunc(), 1) @endcode * @param section_number The section to be processed when the condition is true. */ #define SDV_INTERFACE_SET_SECTION_CONDITION(condition, section_number) \ if (condition) \ selector.Select(section_number); \ } \ if (selector.Selected()) \ { /** * @brief Set section to process. * @param section_number The section to process. */ #define SDV_INTERFACE_SET_SECTION(section_number) \ selector.Select(section_number); \ } \ if (selector.Selected()) \ { /** * @brief Define that the interface map entries following this statement are to be processed regardless of the section * selection. */ #define SDV_INTERFACE_DEFAULT_SECTION() \ } \ selector.DefineDefaultSection(); \ if (selector.Selected()) \ { /** * @brief Define that the interface map entries following this statement belong to a dedicated section and should only * be processed when that section is selected. * @param section_number Begin of a section with the section number. */ #define SDV_INTERFACE_SECTION(section_number) \ } \ selector.DefineSection(section_number); \ if (selector.Selected()) \ { /** * @brief End of interface map. */ #define END_SDV_INTERFACE_MAP() \ } \ return nullptr; /* None found */ \ } // NOLINTEND(cppcoreguidelines-macro-usage) /** * @brief Software Defined Vehicle framework. */ namespace sdv { /** * @brief Interface access wrapper class. * @tparam IInterfaceAccess Interface type to wrap */ class CInterfacePtr { public: /** * @brief Construct a new CInterfacePtr object. * @param[in] pInterface Pointer to the interface to wrap or nullptr when no interface is to be wrapped yet. */ CInterfacePtr(IInterfaceAccess* pInterface = nullptr) : m_pInterface(pInterface) {} /** * @brief Copy construct a new CInterfacePtr object * @param[in] rptrInterface Reference to the CInterfacePtr object to copy from. */ CInterfacePtr(const CInterfacePtr& rptrInterface) : m_pInterface(rptrInterface.m_pInterface.load()) {} /** * @brief Move construct a new CInterfacePtr object * @param[in] rptrInterface Reference to the CInterfacePtr object to move from. */ CInterfacePtr(CInterfacePtr&& rptrInterface) noexcept { IInterfaceAccess* pInterface = rptrInterface.m_pInterface.load(); rptrInterface.m_pInterface = nullptr; m_pInterface = pInterface; } /** * @brief Default destructor. */ virtual ~CInterfacePtr() = default; /** * @brief Assignment operator * @param[in] pInterface Pointer to the interface to wrap or nullptr to clear the wrapping. * @return Returns *this */ CInterfacePtr& operator=(IInterfaceAccess* pInterface) { m_pInterface = pInterface; return *this; } /** * @brief Assignment operator * @param[in] rptrInterface Reference to the CInterfacePtr object to copy from. * @return Returns *this */ CInterfacePtr& operator=(const CInterfacePtr& rptrInterface) { m_pInterface = rptrInterface.m_pInterface.load(); return *this; } /** * @brief Move operator * @param[in] rptrInterface Reference to the CInterfacePtr object to move from. * @return Returns *this */ CInterfacePtr& operator=(CInterfacePtr&& rptrInterface) noexcept { IInterfaceAccess* pInterface = rptrInterface.m_pInterface.load(); rptrInterface.m_pInterface = nullptr; m_pInterface = pInterface; return *this; } /** * @brief Get a pointer to the interface */ operator IInterfaceAccess*() { return m_pInterface; } /** * @brief Get a pointer to the interface */ operator const IInterfaceAccess*() const { return m_pInterface; } /** * @brief Is there a valid interface? * @return Returns 'true' when an interface is wrapped; 'false' otherwise. */ operator bool() const { return static_cast(m_pInterface); } /** * @brief Return whether there is a valid interface. * @return Returns 'true' when an interface is wrapped; 'false' otherwise. */ bool IsValid() const { return static_cast(m_pInterface); } /** * @brief Gets an interface by using the IInterfaceAccess::GetInterface function. * @tparam TIfc The interface type to request. * @return Returns a pointer to the requested interface if the object has itregistered, nullptr otherwise */ template TIfc* GetInterface() const { return m_pInterface ? m_pInterface.load()->GetInterface(GetInterfaceId()).template get() : nullptr; } private: /** * @brief Contained pointer to the interface. */ std::atomic m_pInterface{nullptr}; }; /** * @brief Specialization for sdv::IInterfaceAccess interface. * @tparam TIfc The interface type to request. * @return Returns a pointer to the requested interface if the object has itregistered, nullptr otherwise */ template <> inline IInterfaceAccess* CInterfacePtr::GetInterface() const { return m_pInterface; } /** * @brief IInterfaceAccess smart pointer. */ using TInterfaceAccessPtr = CInterfacePtr; /** * @brief Helper function for interface chaining using a member variable. * @tparam TMember Type of the member variable. * @param[in] rtMember Reference to the member to chain to. * @param[in] idInterface ID of the interface to request. * @return Pointer to the interface. */ template inline sdv::interface_t InterfaceMapChainHelper(TMember& rtMember, interface_id idInterface) { return rtMember.GetInterface(idInterface); } // Warning of cppchgeck for a potential const variable cannot be applied due to the non-const nature of interfaces. Suppress // warning. // cppcheck-suppress constParameterReference /** * @brief Helper function for interface chaining using a member variable. * @tparam IInterfaceAccess Type of the interface wrapped by the member variable. * @param[in] rptrMember Reference to the interface wrapper class to chain to. * @param[in] idInterface ID of the interface to request. * @return Pointer to the interface. */ inline sdv::interface_t InterfaceMapChainHelper(CInterfacePtr& rptrMember, interface_id idInterface) { return rptrMember ? static_cast(rptrMember)->GetInterface(idInterface) : nullptr; } /** * @brief Helper function for interface chaining using a member variable. * @tparam TMember Type of the member variable. * @param[in] ptMember Pointer to the member to chain to. * @param[in] idInterface ID of the interface. * @return Pointer to the interface. */ template inline sdv::interface_t InterfaceMapChainHelper(TMember* ptMember, interface_id idInterface) { return ptMember ? ptMember->GetInterface(idInterface) : nullptr; } /** * @brief Helper function for interface chaining using a member variable. * @tparam TMember Type of the member variable. * @param[in] rptrMember Reference to the shared pointer to the member to chain to. * @param[in] idInterface ID of the interface. * @return Pointer to the interface. */ template inline sdv::interface_t InterfaceMapChainHelper(std::shared_ptr& rptrMember, interface_id idInterface) { return rptrMember ? rptrMember->GetInterface(idInterface) : nullptr; } /** * @brief Helper function for interface chaining using a member variable. * @tparam TMember Type of the member variable. * @param[in] rweakMember Reference to the weak pointer to the member to chain to. * @param[in] idInterface ID of the interface. * @return Pointer to the interface. */ template inline sdv::interface_t InterfaceMapChainHelper(std::weak_ptr& rweakMember, interface_id idInterface) { std::shared_ptr ptrMember = rweakMember.lock(); return InterfaceMapChainHelper(ptrMember, idInterface); } /** * @brief Interface member helper class. * @tparam TInterface The interface the member is to implement. */ template struct SInterfaceMemberHelper { /** * @brief Helper function for interface entries in the interface map. * @tparam TMember Type of the member variable. * @param[in] rtMember Reference to the member implementing the interface. * @param[in] idInterface ID of the interface. * @return Pointer to the interface. */ template static sdv::interface_t InterfaceMapEntryHelper(TMember& rtMember, interface_id idInterface) { static_assert(std::is_same_v || std::is_base_of_v); if (sdv::GetInterfaceId() == idInterface) { return static_cast(&rtMember); } return nullptr; } /** * @brief Helper function for interface entries in the interface map. * @tparam TMember Type of the member variable. * @param[in] ptMember Pointer to the member implementing the interface. * @param[in] idInterface ID of the interface. * @return Pointer to the interface. */ template static sdv::interface_t InterfaceMapEntryHelper(TMember* ptMember, interface_id idInterface) { static_assert(std::is_same_v || std::is_base_of_v); if (sdv::GetInterfaceId() == idInterface) { return static_cast(ptMember); } return nullptr; } /** * @brief Helper function for interface entries in the interface map. * @tparam TMember Type of the member variable. * @param[in] rptrMember Reference to the shared pointer to the member implementing the interface. * @param[in] idInterface ID of the interface. * @return Pointer to the interface. */ template static sdv::interface_t InterfaceMapEntryHelper(std::shared_ptr& rptrMember, interface_id idInterface) { static_assert(std::is_same_v || std::is_base_of_v); if (sdv::GetInterfaceId() == idInterface) { return static_cast(rptrMember.get()); } return nullptr; } /** * @brief Helper function for interface entries in the interface map. * @tparam TMember Type of the member variable. * @param[in] rweakMember Reference to the weak pointer to the member implementing the interface. * @param[in] idInterface ID of the interface. * @return Pointer to the interface. */ template static sdv::interface_t InterfaceMapEntryHelper(std::weak_ptr& rweakMember, interface_id idInterface) { static_assert(std::is_same_v || std::is_base_of_v); std::shared_ptr ptrMember = rweakMember.lock(); return InterfaceMapEntryHelper(ptrMember, idInterface); } }; /** * @brief Helper class to implement the IObjectLifetime interface for objects not supporting this. */ class CObjectLifetimeWrapper : public IInterfaceAccess, public IObjectLifetime, public IObjectDestroy { public: /** * @brief Default constructor */ CObjectLifetimeWrapper() = default; /** * @brief Destructor automatically destroying the object. */ ~CObjectLifetimeWrapper() {} // Interface map BEGIN_SDV_INTERFACE_MAP() SDV_INTERFACE_ENTRY(IObjectLifetime) SDV_INTERFACE_ENTRY(IObjectDestroy) SDV_INTERFACE_CHAIN_MEMBER(m_pObject) END_SDV_INTERFACE_MAP() /** * @brief Create a object lifetime wrapper object. * @details Create an object lifetime wrapper object and assign the provided interface to the wrapper. The wrapper object * blocks calls to IObjectDestroy since it will take ober the management of object lifetime. * @param[in] pObject Interface of the object to wrap. This object needs to expose IObjectDestroy and could expose * IObjectLifetime. * @return Returns an interface giving access to a IObjectLifetime interface, forwarding all other interface calls. */ static IInterfaceAccess* CreateWrapper(IInterfaceAccess* pObject) { if (!pObject) return nullptr; // Nothing to manage std::unique_ptr ptrWrapper = std::make_unique(); // Ignore cppcheck warning; normally the returned pointer should always have a value at this stage (otherwise an // exception was triggered). // cppcheck-suppress knownConditionTrueFalse if (!ptrWrapper) return nullptr; ptrWrapper->Increment(); ptrWrapper->m_pObject = pObject; // Store the pointer internally - this will keep the pointer alive, but invalidates ptrWrapper. std::unique_ptr& rptrKeepAlive = ptrWrapper->m_ptrKeepAlive; rptrKeepAlive = std::move(ptrWrapper); // Return the interface pointer return rptrKeepAlive.get(); } /** * @brief Increment the lifetime. Needs to be balanced by a call to Decrement. Overload of IObjectLifetime::Increment. */ virtual void Increment() override { m_iCounter++; } /** * @brief Decrement the lifetime. If the lifetime reaches zero, the object will be destroyed (through the exposed * IObjectDestroy interface). Overload of IObjectLifetime::Decrement. * @return Returns 'true' if the object was destroyed, false if not. */ virtual bool Decrement() override { if (!m_iCounter) return false; m_iCounter--; if (!m_iCounter) { IObjectDestroy* pObjectDestroy = CInterfacePtr(m_pObject).GetInterface(); if (pObjectDestroy) pObjectDestroy->DestroyObject(); m_pObject = nullptr; m_ptrKeepAlive.reset(); return true; } return false; } /** * Get the current lifetime count. Overload of IObjectLifetime::GetCount. * @remarks The GetCount function returns a momentary value, which can be changed at any moment. * @return Returns the current counter value. */ virtual uint32_t GetCount() const override { return static_cast(m_iCounter); } /** * @brief Destroy the object. Overload of IObjectDestroy::DestroyObject. * @attention After a call of this function, all exposed interfaces render invalid and should not be used any more. */ virtual void DestroyObject() override { // Destroy object is only allowed when the counter is 1. if (static_cast(m_iCounter) != 1) std::cerr << "Trying to destroy an object having references." << std::endl; if (static_cast(m_iCounter) > 0) Decrement(); } IInterfaceAccess* m_pObject = nullptr; ///< The interface pointer. std::atomic m_iCounter = 0; ///< The lifetime counter. std::unique_ptr m_ptrKeepAlive; ///< The one instance of this wrapper class. }; /** * @brief Object smart pointer. Implements destruction of the object. */ class CObjectPtr { public: /** * @brief Construct a new CObjectPtr object. * @param[in] pInterface Pointer to the interface to wrap or nullptr when no interface is to be wrapped yet. */ CObjectPtr(IInterfaceAccess* pInterface = nullptr) { Assign(pInterface); } /** * @brief Copy construct a new CInterfacePtr object * @param[in] rptrInterface Reference to the CInterfacePtr object to copy from. */ CObjectPtr(const CObjectPtr& rptrInterface) : m_ptrObject(rptrInterface.m_ptrObject) { IObjectLifetime* pObjectLifetime = GetInterface(); if (pObjectLifetime) pObjectLifetime->Increment(); } /** * @brief Move construct a new CInterfacePtr object * @param[in] rptrInterface Reference to the CInterfacePtr object to move from. */ CObjectPtr(CObjectPtr&& rptrInterface) noexcept : m_ptrObject(std::move(rptrInterface.m_ptrObject)) {} /** * @brief Destructor. */ virtual ~CObjectPtr() { Clear(); } /** * @brief Assignment operator * @param[in] pInterface Pointer to the interface to wrap or nullptr to clear the wrapping. * @return Returns *this */ CObjectPtr& operator=(IInterfaceAccess* pInterface) { Assign(pInterface); return *this; } /** * @brief Assignment operator * @param[in] rptrInterface Reference to the CInterfacePtr object to copy from. * @return Returns *this */ CObjectPtr& operator=(const CObjectPtr& rptrInterface) { Clear(); m_ptrObject = rptrInterface.m_ptrObject; IObjectLifetime* pObjectLifetime = GetInterface(); if (pObjectLifetime) pObjectLifetime->Increment(); return *this; } /** * @brief Move operator * @param[in] rptrInterface Reference to the CInterfacePtr object to move from. * @return Returns *this */ CObjectPtr& operator=(CObjectPtr&& rptrInterface) noexcept { Attach(rptrInterface.Detach()); return *this; } /** * @brief Get a pointer to the interface */ operator IInterfaceAccess*() { return m_ptrObject; } /** * @brief Get a pointer to the interface */ operator const IInterfaceAccess*() const { return m_ptrObject; } /** * @brief Get a pointer to the interface */ operator CInterfacePtr() const { return m_ptrObject; } /** * @brief Is there a valid interface? * @return Returns 'true' when an interface is wrapped; 'false' otherwise. */ operator bool() const { return IsValid(); } /** * @brief Return whether there is a valid interface. * @return Returns 'true' when an interface is wrapped; 'false' otherwise. */ bool IsValid() const { return static_cast(m_ptrObject); } /** * @brief Gets an interface by using the IInterfaceAccess::GetInterface function. * @tparam TIfc The interface type to request. * @return Returns a pointer to the requested interface if the object has itregistered, nullptr otherwise */ template TIfc* GetInterface() const { return m_ptrObject.GetInterface(); } /** * @brief Assign the object to this object pointer and increment the lifetime counter. * @param[in] pObject Interface pointer to the object. */ void Assign(IInterfaceAccess* pObject) { Clear(); if (!pObject) return; IObjectLifetime* pLifetime = CInterfacePtr(pObject).GetInterface(); if (pLifetime) // Object provides lifetime management. Use this... { m_ptrObject = pObject; pLifetime->Increment(); } else // Create a lifetime management wrapper. Do not increase count... this has already been done during creation m_ptrObject = CObjectLifetimeWrapper::CreateWrapper(pObject); } /** * @brief Destroy the object and clear the interface. */ void Clear() { IObjectLifetime* pLifetime = m_ptrObject.GetInterface(); if (pLifetime) // Object provides lifetime management. Use this... pLifetime->Decrement(); m_ptrObject = nullptr; } /** * @brief Attach an object which is managed by lifetime. The lifetime counter is not increased. * @remarks Attachment only succeeds when the object exposes the IObjectLifetime pointer. * @param[in] pObject Pointer to the object. */ void Attach(IInterfaceAccess* pObject) { Clear(); if (CInterfacePtr(pObject).GetInterface()) m_ptrObject = pObject; } /** * @brief Detach the pointer without change the object lifetime. The internal pointer will beeome NULL. * @attention Detaching the pointer means it is not managed any more. A call to IObjectLifetime::Decrement needs to be done * manually. * @return The detached interface pointer. */ CInterfacePtr Detach() { if (!m_ptrObject) return CInterfacePtr(); CInterfacePtr ptr = std::move(m_ptrObject); m_ptrObject = nullptr; return ptr; } private: CInterfacePtr m_ptrObject; ///< The object pointer. }; /** * @brief IObjectAccess smart pointer. */ using TObjectPtr = CObjectPtr; } // namespace sdv #ifndef DONT_LOAD_CORE_TYPES #include "mem_access.h" #endif #endif // !defined INTERFACE_IMPL_H