// Copyright Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace rocRoller { namespace Component { template concept CSingleUse = requires() { requires static_cast(T::SingleUse) == true; }; /** * @brief A `ComponentBase` is a base class for a category of components. * * - It defines the interface for accessing the implementations. * - All subclasses should provide interchangeable functionality. * */ template concept ComponentBase = requires(T& a) { /** * The Argument type should be either a single type, or a tuple of types needed to * pick the correct component implementation. */ typename T::Argument; requires std::default_initializable>; requires std::copy_constructible; //{ a.name() } -> std::convertible_to; //{ T::Match(c) } -> std::convertible_to; { T::Basename } -> std::convertible_to; }; /** * A function to match whether a given component is appropriate for the situation. * * Only one subclass should match any given situation. */ template using Matcher = std::function; /** * A factory function to create an instance of a particular subclass. */ template using Builder = std::function(typename Base::Argument)>; /** * A concrete subclass which fulfils the required functionality in a subset of situations. */ template concept Component = requires(T a) { typename T::Base; typename T::Base::Argument; // Single-use status can't depend on implementation. requires CSingleUse == CSingleUse; requires std::derived_from; requires ComponentBase; { T::Match } -> std::convertible_to>; { T::Build } -> std::convertible_to>; { T::Name } -> std::convertible_to; { T::Basename } -> std::convertible_to; }; /** * @brief Returns an object of the appropriate subclass of `Base`, based on `ctx`. * May be the same object from one call to the next. */ template requires(!CSingleUse) std::shared_ptr Get(typename Base::Argument const& arg); template requires(!CSingleUse) std::shared_ptr Get(typename Base::Argument&& arg); /** * @brief Convenience function which will construct a tuple and call the above implementation of * May be the same object from one call to the next. */ // clang-format off template requires(!CSingleUse && !std::same_as && std::constructible_from) std::shared_ptr Get(Arg0 const& arg0, Args const&... args); // clang-format on /** * @brief Returns a new instance object of the appropriate subclass of `Base`, based * on `ctx`. */ template std::shared_ptr GetNew(typename Base::Argument const& arg); template std::shared_ptr GetNew(typename Base::Argument&& arg); // clang-format off template requires(!std::same_as && std::constructible_from) std::shared_ptr GetNew(Arg0 const& arg0, Args const&... args); // clang-format on template bool RegisterComponentImpl(); class ComponentFactoryBase { public: static void ClearAllCaches(); virtual void emptyCache() = 0; static void RegisterBase(ComponentFactoryBase* base); private: inline static std::unordered_set m_instances; }; template class ComponentFactory : public ComponentFactoryBase, LazySingleton> { public: using Argument = typename Base::Argument; struct Entry { std::string name; Matcher matcher; Builder builder; }; ComponentFactory(); static ComponentFactory& Instance(); template std::shared_ptr get(T&& arg) const; template std::shared_ptr getNew(T&& arg) const; template Entry const& getEntry(T&& arg) const; template bool registerComponent(); bool registerComponent(std::string const& name, Matcher matcher, Builder builder); void registerImplementations(); template void emptyCache(T&& arg); virtual void emptyCache() override; private: using ReaderLock = std::shared_lock; using WriterLock = std::unique_lock; mutable std::shared_mutex m_entriesLock; std::vector m_entries; /** * Finds an entry among the registered entries (classes). This is the fallback for if there * is no entry in the cache. */ template Entry const& findEntry(T&& arg) const; mutable std::shared_mutex m_entryCacheLock; mutable std::unordered_map m_entryCache; mutable std::shared_mutex m_instanceCacheLock; mutable std::unordered_map> m_instanceCache; }; } } #include