Program Listing for File component_impl.h#

Return to documentation for file (support\component_impl.h)

#ifndef COMPONENT_IMPL_H
#define COMPONENT_IMPL_H

#include <cstring>
#include <mutex>
#include <shared_mutex>
#include <vector>

#include "../interfaces/core_types.h"
#include "../interfaces/core.h"
#include "../interfaces/repository.h"
#include "interface_ptr.h"
#include "local_service_access.h"

// NOLINTBEGIN(cppcoreguidelines-macro-usage)

#define SDV_STRINGIZE_HELPER2(txt) #txt
#define SDV_STRINGIZE_HELPER(txt) SDV_STRINGIZE_HELPER2(txt)

 // NOLINTEND(cppcoreguidelines-macro-usage)

#ifdef _WIN32

// Resolve conflict
#pragma push_macro("interface")
#undef interface
#pragma push_macro("GetObject")
#undef GetObject

#ifndef NOMINMAX
#define NOMINMAX
#endif

#include <WinSock2.h>
#include <Windows.h>
#include <objbase.h>

// Resolve conflict
#pragma pop_macro("GetObject")
#pragma pop_macro("interface")
#ifdef GetClassInfo
#undef GetClassInfo
#endif

#elif defined __unix__
#else
#error OS is not supported!
#endif // defined _MSC_VER


namespace sdv
{
    // Forward declarations
    class CSdvObject;


    // Object control classes         //

    struct ISdvObjectClassInfo
    {
    public:
        virtual sdv::SClassInfo GetClassInfo() const = 0;

        virtual std::unique_ptr<CSdvObject> CreateObject() = 0;
    };

    class CSdvObjectAccess : public IInterfaceAccess
    {
    public:
        CSdvObjectAccess() = default;

        CSdvObjectAccess(CSdvObjectAccess&)             = delete;
        CSdvObjectAccess& operator=(CSdvObjectAccess&)  = delete;
        CSdvObjectAccess(CSdvObjectAccess&&)            = delete;
        CSdvObjectAccess& operator=(CSdvObjectAccess&&) = delete;

        virtual ~CSdvObjectAccess() = default;

        IInterfaceAccess* GetObjectAccess()
        {
            return static_cast<IInterfaceAccess*>(this);
        }

        // Interface map
        BEGIN_SDV_INTERFACE_MAP()
        END_SDV_INTERFACE_MAP()
    };

    class CLifetimeCookie
    {
    public:
        CLifetimeCookie() = default;

        CLifetimeCookie(std::function<void()> fnLifetimeIncr, std::function<void()> fnLifetimeDecr) :
            m_fnLifetimeIncr(fnLifetimeIncr), m_fnLifetimeDecr(fnLifetimeDecr)
        {
            if (m_fnLifetimeIncr) m_fnLifetimeIncr();
        }

        CLifetimeCookie(const CLifetimeCookie& rCookie) :
            m_fnLifetimeIncr(rCookie.m_fnLifetimeIncr), m_fnLifetimeDecr(rCookie.m_fnLifetimeDecr)
        {
            if (m_fnLifetimeIncr) m_fnLifetimeIncr();
        }

        CLifetimeCookie(CLifetimeCookie&& rCookie) noexcept :
            m_fnLifetimeIncr(rCookie.m_fnLifetimeIncr), m_fnLifetimeDecr(rCookie.m_fnLifetimeDecr)
        {
            rCookie.m_fnLifetimeIncr = nullptr;
            rCookie.m_fnLifetimeDecr = nullptr;
        }

        ~CLifetimeCookie()
        {
            if (m_fnLifetimeDecr) m_fnLifetimeDecr();
        }

        CLifetimeCookie& operator=(const CLifetimeCookie& rCookie)
        {
            if (m_fnLifetimeDecr) m_fnLifetimeDecr();
            m_fnLifetimeIncr = rCookie.m_fnLifetimeIncr;
            m_fnLifetimeDecr = rCookie.m_fnLifetimeDecr;
            if (m_fnLifetimeIncr) m_fnLifetimeIncr();
            return *this;
        }

        CLifetimeCookie& operator=(CLifetimeCookie&& rCookie) noexcept
        {
            if (m_fnLifetimeDecr) m_fnLifetimeDecr();
            m_fnLifetimeIncr = rCookie.m_fnLifetimeIncr;
            m_fnLifetimeDecr = rCookie.m_fnLifetimeDecr;
            rCookie.m_fnLifetimeIncr = nullptr;
            rCookie.m_fnLifetimeDecr = nullptr;
            return *this;
        }

    private:
        std::function<void()>   m_fnLifetimeIncr;
        std::function<void()>   m_fnLifetimeDecr;
    };

    class CObjectFactory : public IInterfaceAccess, public IObjectFactory
    {
        // Friend class SDV object and definition
        friend class CSdvObject;
        template <class TSdvObject>
        friend class CSdvObjectClass;

    public:
        CObjectFactory() = default;

        BEGIN_SDV_INTERFACE_MAP()
            SDV_INTERFACE_ENTRY(IObjectFactory)
        END_SDV_INTERFACE_MAP()

        CLifetimeCookie CreateLifetimeCookie()
        {
            return CLifetimeCookie([this]() { m_uiActiveObjectCount++; },
                [this]() { if (m_uiActiveObjectCount) m_uiActiveObjectCount--; });
        }

        uint32_t GetActiveObjects() const
        {
            return m_uiActiveObjectCount;
        }

        const char* GetManifest()
        {
            if (m_ssManifest.empty())
                BuildManifest();
            return m_ssManifest.c_str();
        }

        virtual IInterfaceAccess* CreateObject(const sdv::u8string& ssClassName) override;

        virtual void DestroyObject(IInterfaceAccess* object) override;

        virtual void DestroyAllObjects() override;

    protected:
        void BuildManifest()
        {
            std::stringstream sstream;
            sstream << R"code(# Module manifest

[Interface]
Version = )code" << SDVFrameworkInterfaceVersion
                    << R"code(

)code";
            std::shared_lock<std::shared_mutex> lock(m_mtxObjectClasses);
            for (const ISdvObjectClassInfo* pClassInfo : m_vecObjectClasses)
            {
                sdv::SClassInfo sInfo = pClassInfo->GetClassInfo();
                sstream << "[[Component]]" << std::endl;
                sstream << "Class=\"" << sInfo.ssClassName << "\"" << std::endl;
                if (!sInfo.seqClassAliases.empty())
                {
                    sstream << "Aliases=[";
                    bool bInitialAlias = true;
                    for (const sdv::u8string& rssAlias : sInfo.seqClassAliases)
                    {
                        if (!bInitialAlias)
                            sstream << ", ";
                        bInitialAlias = false;
                        sstream << "\"" << rssAlias << "\"";
                    }
                    sstream << "]" << std::endl;
                }
                if (!sInfo.ssDefaultObjectName.empty())
                    sstream << "DefaultName=\"" << sInfo.ssDefaultObjectName << "\"" << std::endl;
                bool bSkip = false;
                switch (sInfo.eType)
                {
                case sdv::EObjectType::SystemObject:
                    sstream << "Type=\""
                            << "System"
                            << "\"" << std::endl;
                    break;
                case sdv::EObjectType::Device:
                    sstream << "Type=\""
                            << "Device"
                            << "\"" << std::endl;
                    break;
                case sdv::EObjectType::BasicService:
                    sstream << "Type=\""
                            << "BasicService"
                            << "\"" << std::endl;
                    break;
                case sdv::EObjectType::ComplexService:
                    sstream << "Type=\""
                            << "ComplexService"
                            << "\"" << std::endl;
                    break;
                case sdv::EObjectType::Application:
                    sstream << "Type=\""
                            << "App"
                            << "\"" << std::endl;
                    break;
                case sdv::EObjectType::Proxy:
                    sstream << "Type=\""
                            << "Proxy"
                            << "\"" << std::endl;
                    break;
                case sdv::EObjectType::Stub:
                    sstream << "Type=\""
                            << "Stub"
                            << "\"" << std::endl;
                    break;
                case sdv::EObjectType::Utility:
                    sstream << "Type=\""
                            << "Utility"
                            << "\"" << std::endl;
                    break;
                default:
                    bSkip = true;
                    break;
                }
                if (bSkip)
                    continue;
                if (sInfo.uiFlags & static_cast<uint32_t>(sdv::EObjectFlags::singleton))
                    sstream << "Singleton=true" << std::endl;
                if (!sInfo.seqDependencies.empty())
                {
                    sstream << "Dependencies=[";
                    bool bInitialDependency = true;
                    for (const sdv::u8string& rssDependsOn : sInfo.seqDependencies)
                    {
                        if (!bInitialDependency)
                            sstream << ", ";
                        bInitialDependency = false;
                        sstream << "\"" << rssDependsOn << "\"";
                    }
                    sstream << "]" << std::endl;
                }
            }
            m_ssManifest = sstream.str();
        }

        void ExposeObjectClass(ISdvObjectClassInfo* pObjectClassInfo)
        {
            if (pObjectClassInfo)
            {
                std::unique_lock<std::shared_mutex> lock(m_mtxObjectClasses);
                m_vecObjectClasses.push_back(pObjectClassInfo);

                // Attention: pObjectClass is a pointer to ISdvObjectClassInfo even if the class was derived.
                // Virtual functions are not available yet at this stage.
            }
        }

        void RevokeObjectClass(const ISdvObjectClassInfo* pObjectClassInfo)
        {
            std::unique_lock<std::shared_mutex> lock(m_mtxObjectClasses);
            auto itObjectClass = std::find(m_vecObjectClasses.begin(), m_vecObjectClasses.end(), pObjectClassInfo);
            if (itObjectClass != m_vecObjectClasses.end())
                m_vecObjectClasses.erase(itObjectClass);
            // TODO EVE: Updated through cppcheck warning
            //for (auto objectClassIter = m_vecObjectClasses.begin(); objectClassIter != m_vecObjectClasses.end();
            //     objectClassIter++)
            //{
            //    if (*objectClassIter == pObjectClassInfo)
            //    {
            //        m_vecObjectClasses.erase(objectClassIter);
            //        break;
            //    }
            //}
        }

    private:
        std::atomic<uint32_t>                       m_uiActiveObjectCount{0};

        mutable std::mutex                          m_mtxActiveObjects;
        std::vector<std::unique_ptr<CSdvObject>>    m_vecActiveObjects;

        mutable std::shared_mutex                   m_mtxObjectClasses;
        std::vector<ISdvObjectClassInfo*>           m_vecObjectClasses;

        std::string                                 m_ssManifest;
    };

    // Framework module classes       //

    class CModule
        : public CSdvObjectAccess
        , public CObjectFactory
    {
    public:
        CModule() = default;

        BEGIN_SDV_INTERFACE_MAP()
            SDV_INTERFACE_CHAIN_BASE(CObjectFactory)
        END_SDV_INTERFACE_MAP()

        IInterfaceAccess* GetModuleControl(uint32_t interfaceVersion)
        {
            // No compatibility interfacing available yet.
            return interfaceVersion == SDVFrameworkInterfaceVersion ? GetObjectAccess() : nullptr;
        }
    };

    inline CModule& GetModule()
    {
        static CModule module;
        return module;
    }

    inline CLifetimeCookie CreateLifetimeCookie()
    {
        return GetModule().CreateLifetimeCookie();
    }

    // Component object class //

    template <class TSdvObject>
    class CSdvObjectClass
        : public ISdvObjectClassInfo
    {
    public:
        CSdvObjectClass()
        {
            // Add this object definition to the definition list.
            GetModule().ExposeObjectClass(this);
        }

        CSdvObjectClass(CSdvObjectClass&)             = delete;
        CSdvObjectClass(CSdvObjectClass&&)            = delete;
        CSdvObjectClass& operator=(CSdvObjectClass&)  = delete;
        CSdvObjectClass& operator=(CSdvObjectClass&&) = delete;

        virtual ~CSdvObjectClass()
        {
            GetModule().RevokeObjectClass(this);
        }

        virtual sdv::u8string GetClassName() const
        {
            return TSdvObject::GetClassNameStatic();
        }

        virtual sdv::sequence<sdv::u8string> GetClassAliases() const
        {
            return TSdvObject::GetClassAliasesStatic();
        }

        virtual sdv::u8string GetDefaultObjectName() const
        {
            return TSdvObject::GetDefaultObjectNameStatic();
        }

        virtual bool IsSingleton() const
        {
            return TSdvObject::IsSingletonStatic();
        }

        virtual sdv::sequence<sdv::u8string> GetObjectDependencies() const
        {
            return TSdvObject::GetObjectDependenciesStatic();
        }

        virtual EObjectType GetObjectType() const
        {
            return TSdvObject::GetObjectType();
        }

    protected:
        uint32_t GetObjectFlags() const
        {
            uint32_t flags = 0;

            if (IsSingleton()) flags |= static_cast<uint32_t>(EObjectFlags::singleton);

            // Currently no other flags known.

            return flags;
        }

        sdv::SClassInfo GetClassInfo() const override
        {
            sdv::SClassInfo classInfo{};
            classInfo.ssClassName = GetClassName();
            classInfo.seqClassAliases = GetClassAliases();
            classInfo.ssDefaultObjectName = GetDefaultObjectName();
            classInfo.eType = GetObjectType();
            classInfo.uiFlags = GetObjectFlags();
            classInfo.seqDependencies = GetObjectDependencies();
            return classInfo;
        }

        std::unique_ptr<CSdvObject> CreateObject() override
        {
            std::unique_ptr<CSdvObject> ret;
            try
            {
                ret = std::make_unique<TSdvObject>();
            }
            catch(...)
            {
                SDV_LOG(core::ELogSeverity::error, "Failed to instantiate object of class ", GetClassName(), " - exception thrown during construction! ");
            }
            return ret;
        }
    };

    class CSdvObject : public CSdvObjectAccess
    {
    public:
        template <class TSdvObject>
        using TSdvObjectCreator = CSdvObjectClass<TSdvObject>;

        CSdvObject() = default;

        CSdvObject(CSdvObject&) = delete;
        CSdvObject& operator=(CSdvObject&) = delete;
        CSdvObject(CSdvObject&&) = delete;
        CSdvObject& operator=(CSdvObject&&) = delete;

        ~CSdvObject() override
        {}

        static sdv::sequence<sdv::u8string> GetClassAliasesStatic() { return {}; }

        static sdv::u8string GetDefaultObjectNameStatic() { return {}; }

        static bool IsSingletonStatic() { return false; }

        static sdv::sequence<sdv::u8string> GetObjectDependenciesStatic() { return sdv::sequence<sdv::u8string>(); }

        BEGIN_SDV_INTERFACE_MAP()
        END_SDV_INTERFACE_MAP()
    };

    // Object control class implementation //

    inline IInterfaceAccess* CObjectFactory::CreateObject(const sdv::u8string& ssClassName)
    {
        if (ssClassName.empty())
        {
            return nullptr;
        }

        std::shared_lock<std::shared_mutex> lock(m_mtxObjectClasses);
        for (ISdvObjectClassInfo* pObjectClassInfo : m_vecObjectClasses)
        {
            if (pObjectClassInfo == nullptr)continue;
            sdv::SClassInfo sClassInfo = pObjectClassInfo->GetClassInfo();

            // Check for the class name.
            bool bFound = sClassInfo.ssClassName == ssClassName;

            // If not found, check for all aliases.
            for (auto itAlias = sClassInfo.seqClassAliases.begin(); !bFound && itAlias != sClassInfo.seqClassAliases.end(); itAlias++)
                bFound = *itAlias == ssClassName;
            if (!bFound) continue;

            ++m_uiActiveObjectCount;
            auto object = pObjectClassInfo->CreateObject();
            lock.unlock();
            if(!object)
            {
                --m_uiActiveObjectCount;
                return nullptr;
            }
            auto ret = object.get()->GetObjectAccess();
            std::unique_lock<std::mutex> lockObjects (m_mtxActiveObjects);
            m_vecActiveObjects.emplace_back(std::move(object));
            return ret;
        }

        return nullptr;
    }

    inline void CObjectFactory::DestroyObject(IInterfaceAccess* object)
    {
        if (object == nullptr)
        {
            return;
        }

        std::unique_lock<std::mutex> lockObjects(m_mtxActiveObjects);
        for(auto iter = m_vecActiveObjects.begin(); iter!= m_vecActiveObjects.end();++iter)
        {
            if(iter->get()->GetObjectAccess() == object)
            {
                auto objectPtr = std::move(*iter);
                m_vecActiveObjects.erase(iter);
                lockObjects.unlock();
                objectPtr = nullptr;
                if (m_uiActiveObjectCount) --m_uiActiveObjectCount;
                return;
            }
        }

    }

    inline void CObjectFactory::DestroyAllObjects()
    {
        std::unique_lock<std::mutex> lockObjects(m_mtxActiveObjects);
        auto objects = std::move(m_vecActiveObjects);
        lockObjects.unlock();
        while(!objects.empty())
        {
            objects.pop_back();
            if (m_uiActiveObjectCount) --m_uiActiveObjectCount;
        }
    }
} // namespace sdv

// NOLINTBEGIN(cppcoreguidelines-macro-usage)

#define DEFINE_SDV_OBJECT(sdv_object_class)                                                                                        \
    struct SObjectClassInstance_##sdv_object_class                                                                                 \
    {                                                                                                                              \
    public:                                                                                                                        \
        SObjectClassInstance_##sdv_object_class()                                                                                  \
        {                                                                                                                          \
            /* Enforce derivation of sdv::CSdvObject. */                                                                           \
            static_assert(std::is_base_of<sdv::CSdvObject, sdv_object_class>::value,                                               \
                          "CSdvObject is not base of sdv_object_class");                                                           \
            /* Call the static function once to instantiate the definition. */                                                     \
            GetObjectClassInstance();                                                                                              \
        }                                                                                                                          \
        static sdv_object_class::TSdvObjectCreator<sdv_object_class>& GetObjectClassInstance()                                     \
        {                                                                                                                          \
            static sdv_object_class::TSdvObjectCreator<sdv_object_class> object_class;                                             \
            return object_class;                                                                                                   \
        }                                                                                                                          \
    };                                                                                                                             \
    static SObjectClassInstance_##sdv_object_class g_##sdv_object_class;                                                           \
    extern "C" SDV_SYMBOL_PUBLIC bool HasActiveObjects();                                                                          \
    extern "C" SDV_SYMBOL_PUBLIC sdv::IInterfaceAccess* GetModuleFactory(uint32_t uiInterfaceVersion);                             \
    extern "C" SDV_SYMBOL_PUBLIC const char* GetManifest();

#define DEFINE_SDV_OBJECT_NO_EXPORT(sdv_object_class)                                                                              \
    struct SObjectClassInstance_##sdv_object_class                                                                                 \
    {                                                                                                                              \
    public:                                                                                                                        \
        SObjectClassInstance_##sdv_object_class()                                                                                  \
        {                                                                                                                          \
            /* Enforce derivation of sdv::CSdvObject. */                                                                           \
            static_assert(std::is_base_of<sdv::CSdvObject, sdv_object_class>::value,                                               \
                          "CSdvObject is not base of sdv_object_class");                                                           \
            /* Call the static function once to instantiate the definition. */                                                     \
            GetObjectClassInstance();                                                                                              \
        }                                                                                                                          \
        static sdv_object_class::TSdvObjectCreator<sdv_object_class>& GetObjectClassInstance()                                     \
        {                                                                                                                          \
            static sdv_object_class::TSdvObjectCreator<sdv_object_class> object_class;                                             \
            return object_class;                                                                                                   \
        }                                                                                                                          \
    };                                                                                                                             \
    static SObjectClassInstance_##sdv_object_class g_##sdv_object_class;                                                           \
    bool HasActiveObjects();                                                                                                       \
    sdv::IInterfaceAccess* GetModuleFactory(uint32_t uiInterfaceVersion);                                                          \
    const char* GetManifest();

#define DECLARE_OBJECT_CLASS_TYPE(class_type)                                                                                      \

                                                                                                                            \
    constexpr static sdv::EObjectType GetObjectType()                                                                              \
    {                                                                                                                              \
        return class_type;                                                                                                         \
    }

#define DECLARE_OBJECT_CLASS_NAME(class_name_string)                                                                               \
    static sdv::u8string GetClassNameStatic()                                                                                      \
    {                                                                                                                              \
        return class_name_string;                                                                                                  \
    }

#define DECLARE_OBJECT_CLASS_ALIAS(...)                                                                                            \
    static sdv::sequence<sdv::u8string> GetClassAliasesStatic()                                                                    \
    {                                                                                                                              \
        return sdv::sequence<sdv::u8string>({__VA_ARGS__});                                                                        \
    }

#define DECLARE_DEFAULT_OBJECT_NAME(object_name_string)                                                                            \
    static sdv::u8string GetDefaultObjectNameStatic()                                                                              \
    {                                                                                                                              \
        return object_name_string;                                                                                                 \
    }

#define DECLARE_OBJECT_SINGLETON()                                                                                                 \
    static bool IsSingletonStatic() { return true; }

#define DECLARE_OBJECT_DEPENDENCIES(...)                                                                                           \
    static sdv::sequence<sdv::u8string> GetObjectDependenciesStatic() { return sdv::sequence<sdv::u8string>({__VA_ARGS__}); }

 // NOLINTEND(cppcoreguidelines-macro-usage)

/*
 * @brief Returns whether or not instances of objects implemented by this module are running. If none, the module can be unloaded.
 * @remarks Unloading the module with running instances could cause a crash and should be prevented at all costs. Unloading the
 * module removes the code of the objects still running.
 * @return Returns true when object instances are running; otherwise returns 'false'.
 */
inline extern bool HasActiveObjects()
{
    return sdv::GetModule().GetActiveObjects() != 0;
}

/*
 * @brief Get the module factory interface with a specific version.
 * @details This function provides access to the objects being implemented in this module.
 * @param[in] interfaceVersion Request the module factory for a specific interface version. Using another interface version than
 * the one returned from the manifest, might cause the function to fail.
 * @return Returns pointer to the IInterfaceAccess interface of the module factory object.
 */
inline extern sdv::IInterfaceAccess* GetModuleFactory(uint32_t interfaceVersion)
{
    return sdv::GetModule().GetModuleControl(interfaceVersion);
}

/*
 * @brief Get the the module manifest.
 * @details Each module contains a manifest containing general information as well as information about the component classes. This
 * allows installing the component without having to instantiate the classes.
 * @return Returns the pointer to a zero terminated string containing the module manifest or NULL when there is no string.
 */
inline extern const char* GetManifest()
{
    return sdv::GetModule().GetManifest();
}

#endif // !defined COMPONENT_IMPL_H