diff --git a/CMakeLists.txt b/CMakeLists.txt index 05a1b39c..14e65800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,9 @@ set(IGN_CMAKE_VER ${ignition-cmake3_VERSION_MAJOR}) #============================================================================ # Configure the project #============================================================================ -ign_configure_project(VERSION_SUFFIX pre0) +ign_configure_project( + REPLACE_IGNITION_INCLUDE_PATH gz/plugin + VERSION_SUFFIX pre0) #============================================================================ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index fd239c4d..75cfc2a8 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -17,4 +17,5 @@ ign_build_tests( SOURCES ${tests}) -add_subdirectory(include/ignition/plugin) +add_subdirectory(include/gz/plugin) +install(DIRECTORY include/ignition DESTINATION ${IGN_INCLUDE_INSTALL_DIR_FULL}) diff --git a/core/include/ignition/plugin/CMakeLists.txt b/core/include/gz/plugin/CMakeLists.txt similarity index 100% rename from core/include/ignition/plugin/CMakeLists.txt rename to core/include/gz/plugin/CMakeLists.txt diff --git a/core/include/gz/plugin/EnablePluginFromThis.hh b/core/include/gz/plugin/EnablePluginFromThis.hh new file mode 100644 index 00000000..6deaf4d9 --- /dev/null +++ b/core/include/gz/plugin/EnablePluginFromThis.hh @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GZ_PLUGIN_ENABLEPLUGINFROMTHIS_HH_ +#define GZ_PLUGIN_ENABLEPLUGINFROMTHIS_HH_ + +#include + +#include + +namespace ignition +{ + namespace plugin + { + // Forward declaration + class Loader; + + /// \brief EnablePluginFromThis is an optional base class which can be + /// inherited by Plugin classes. When a Plugin class inherits it and that + /// Plugin class is instantiated using the Loader class, its instance will + /// be able to access the PluginPtr that manages its lifecycle. This + /// interface will also be available by calling + /// `instance->QueryInterface()` + /// + /// \remark This class is analogous to std::enable_shared_from_this + class IGNITION_PLUGIN_VISIBLE EnablePluginFromThis + { + /// \brief Default constructor + public: EnablePluginFromThis(); + + /// \brief Get a copy of the PluginPtr that manages this object. + /// \return a copy of the PluginPtr that manages this object. + public: PluginPtr PluginFromThis(); + + /// \brief Get a const-qualified copy of the PluginPtr that manages this + /// object. + /// \return a const-qualified copy of the PluginPtr that manages this + /// object. + public: ConstPluginPtr PluginFromThis() const; + + /// \brief Destructor + public: virtual ~EnablePluginFromThis(); + + /// \brief This is an abstract shared_ptr to the Plugin instance. This + /// shared_ptr will maintain a reference count on the shared library that + /// provides the plugin so that the shared library does not get unloaded + /// while this shared_ptr is still alive. + /// + /// This function is meant for advanced use-cases, such as creating + /// Factory plugins, so we keep it protected. Only use this if you know + /// what you are doing. + /// + /// \return shared_ptr to the Plugin instance. + protected: std::shared_ptr PluginInstancePtrFromThis() const; + + // Declare friendship so that the internal WeakPluginPtr can be set by + // the Loader class. + friend class Loader; + + /// \brief This function is called by the Loader class whenever a plugin + /// containing this interface gets instantiated. + private: void PrivateSetPluginFromThis(const PluginPtr &_ptr); + + private: class Implementation; + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + private: std::unique_ptr pimpl; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + }; + } +} + +#endif diff --git a/core/include/gz/plugin/Factory.hh b/core/include/gz/plugin/Factory.hh new file mode 100644 index 00000000..0b533c78 --- /dev/null +++ b/core/include/gz/plugin/Factory.hh @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2018 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef GZ_PLUGIN_FACTORY_HH_ +#define GZ_PLUGIN_FACTORY_HH_ + +#include +#include +#include +#include + +#include + +namespace ignition +{ + namespace plugin + { + /// \brief This class provides a unary operator for safely deleting pointers + /// to plugin factory products with the type `Interface`. If it gets passed + /// an `Interface` pointer that is not pointing to a factory plugin product, + /// then this just performs a normal delete. + template class ProductDeleter; + + /// \brief ProductPtr is a derivative of std::unique_ptr that can safely + /// manage the products that come out of a plugin factory. It is strongly + /// recommended that factory products use a ProductPtr to manage the + /// lifecycle of a factory product. + /// + /// If you MUST release a factory product pointer, then it should at least + /// be passed to a ProductDeleter to be deleted at the end of its lifecycle. + /// If it is not possible to delete the product with a ProductDeleter, then + /// you will need to periodically call CleanupLostProducts() or else you + /// will experience memory leaks, and your factory plugin libraries will + /// never get unloaded. + template + using ProductPtr = + std::unique_ptr>; + + /// \brief The Factory class defines a plugin factory that can be used by + /// the Loader class to produce products that implement an interface. + /// + /// To define the inputs and outputs of a factory, set the template + /// arguments, for example: + /// + /// \code + /// using MyFactory = Factory; + /// \endcode + /// + /// defines a factory that can produce a `std::unique_ptr` + /// given arguments of `InputType1` and `InputType2`. + /// + /// To register a factory, use the `IGNITION_ADD_FACTORY` macro, e.g.: + /// + /// \code + /// IGNITION_ADD_FACTORY(ImplementedClass, MyFactory) + /// \endcode + /// + /// where `ImplementedClass` is the name of the class that your plugin + /// library has used to implement `InterfaceClass`. + template + class Factory : public EnablePluginFromThis + { + public: using ProductPtrType = ProductPtr; + + /// \brief This function is called by Loader to construct the class that + /// implements the InterfacePtr interface. + /// \param[in] _args + /// The arguments as defined by the template parameters. + /// \return an RAII-managed reference to the interface type as defined by + /// the template parameters. + public: ProductPtrType Construct(Args&&... _args); + + /// \internal \brief This function gets implemented by Producing + /// to manufacture the product instance. + /// \param[in] _args + /// The arguments as defined by the template parameters + /// \return a raw pointer to the product + private: virtual Interface *ImplConstruct(Args&&... _args) = 0; + + /// \private This nested class is used to implement the plugin factory. + /// It is not intended for external use. + public: template + class Producing; + }; + + /// \brief Call this function to cleanup the Factories of any Products which + /// were not managed by a ProductPtr or deleted by a ProductDeleter (in + /// other words, the Product was released from its ProductPtr and then its + /// lifecycle was managed by a framework that does not know it has special + /// deletion requirements). + /// + /// If you never call the .release() function on a ProductPtr, then you will + /// never need to call this function. + /// + /// \warning Note that this function should be called ONLY while there are + /// no Products that are actively destructing, or else there is a miniscule + /// probability of causing a segmentation fault. This is never an issue in a + /// single-threaded application. + /// + /// \warning If you have a multi-threaded application where you have + /// absolutely no control over the lifecycle of the products, and you cannot + /// reliably predict a safe window in which you know that no products are + /// being actively deleted, then you can specify a short safety wait to the + /// cleanup call. If any products were being deleted while this function is + /// called, this wait can give them time to fully exit their destructors + /// before we unload their libraries. + /// + /// \note For some applications, it might not be important if there are tiny + /// memory leaks or if plugin libraries remain loaded until the application + /// exits. For those applications, it is okay to not bother calling this + /// function at all. + /// + /// \param[in] _safetyWait + /// For multi-threaded applications, this short waiting window gives time + /// for products that are currently being deleted to exit their + /// destructors before we unload their libraries. If you can reliably + /// predict a window of time in which no products are actively being + /// destructed (or if you have a single-threaded application), then it is + /// okay to set this waiting time to 0. Note that any threads which are + /// about to destruct a product will be blocked until this wait is over. + void IGNITION_PLUGIN_VISIBLE CleanupLostProducts( + const std::chrono::nanoseconds &_safetyWait = + std::chrono::nanoseconds(5)); + + /// \brief Get the number of lost products that have currently accumulated + /// since the last time CleanupLostProducts() was called (or since the + /// program began, if CleanupLostProducts() has not been called yet). + std::size_t IGNITION_PLUGIN_VISIBLE LostProductCount(); + } +} + +#include + +#endif diff --git a/core/include/gz/plugin/Info.hh b/core/include/gz/plugin/Info.hh new file mode 100644 index 00000000..736f99ad --- /dev/null +++ b/core/include/gz/plugin/Info.hh @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_INFO_HH_ +#define GZ_PLUGIN_INFO_HH_ + +#include +#include +#include +#include +#include + +#include + +#include + +namespace ignition +{ + namespace plugin + { + /// \brief sentinel value to check if a plugin was built with the same + /// version of the Info struct + // + /// This must be incremented when the Info struct changes + const int INFO_API_VERSION = 1; + + // We use an inline namespace to assist in forward-compatibility. Eventually + // we may want to support a version-2 of the Info API, in which case + // we will remove the "inline" declaration here, and create a new inline + // namespace called "v2". This original Info object will continue to + // be accessible for backwards compatibility, and even its symbol name in + // the ABI should remain the same. + inline namespace v1 + { + /// \brief Holds info required to construct a plugin + struct IGNITION_PLUGIN_VISIBLE Info + { + /// \brief Clear out all information contained in this Info object + void Clear(); + + /// \brief The name of the plugin + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + std::string name; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + + /// \brief Alternative names that may be used to instantiate the plugin + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + std::set aliases; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + + /// \brief The keys are the names of the types of interfaces that this + /// plugin provides. The values are functions that convert a void + /// pointer (which actually points to the plugin instance) to another + /// void pointer (which actually points to the location of the interface + /// within the plugin instance). + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + using InterfaceCastingMap = + std::unordered_map< std::string, std::function >; + InterfaceCastingMap interfaces; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + + /// \brief This is a set containing the demangled versions of the names + /// of the interfaces provided by this plugin. This gets filled in by + /// the Loader after receiving the Info. It is only used by + /// the user-facing API. Internally, when looking up Interfaces, the + /// mangled `interfaces` map will still be used. + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + std::set demangledInterfaces; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + + /// \brief A method that instantiates a new instance of a plugin + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + std::function factory; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + + /// \brief A method that safely deletes an instance of the plugin + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + std::function deleter; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + }; + } + + /// This typedef is used simultaneously by detail/Register.hh and Loader.cc, + /// so we store it in a location that is visible to both of them. + using InfoMap = std::unordered_map; + using ConstInfoPtr = std::shared_ptr; + } +} + +#endif diff --git a/core/include/gz/plugin/Plugin.hh b/core/include/gz/plugin/Plugin.hh new file mode 100644 index 00000000..71c3abc8 --- /dev/null +++ b/core/include/gz/plugin/Plugin.hh @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_PLUGIN_HH_ +#define GZ_PLUGIN_PLUGIN_HH_ + +#include +#include +#include + +#include + +#include +#include + +namespace ignition +{ + namespace plugin + { + // Forward declaration + namespace detail { + template class ComposePlugin; + template class SelectSpecializers; + } + class EnablePluginFromThis; + class WeakPluginPtr; + + class IGNITION_PLUGIN_VISIBLE Plugin + { + // -------------------- Public API --------------------- + + /// \brief Get an interface of the specified type, if it is provided by + /// this plugin. + /// + /// Note that the interface pointer you receive is owned by the Plugin + /// object. You MUST NOT ever try to deallocate it yourself. Moreover, the + /// pointer will be invalidated once all Plugin objects that refer to the + /// same Plugin instance are destructed. Use the QueryInterfaceSharedPtr + /// function in order to get a reference-counting pointer to an interface + /// of this Plugin object. The pointer will remain valid as long as the + /// std::shared_ptr provided by QueryInterfaceSharedPtr is alive. + /// + /// \return A raw pointer to the specified interface. If the requested + /// _interfaceName is not provided by this Plugin, this returns a nullptr. + /// This pointer is invalidated when the reference count of the plugin + /// instance drops to zero. + public: template + Interface *QueryInterface(); + + /// \brief const-qualified version of QueryInterface() + public: template + const Interface *QueryInterface() const; + + /// \brief Get the requested interface as a std::shared_ptr. The template + /// argument Interface must exactly match the underlying type associated + /// with _interfaceName, or else the behavior of this function is + /// undefined. + /// + /// This std::shared_ptr and the interface+plugin that it refers to will + /// remain valid, even if all Plugin objects which refer to the plugin + /// instance are destructed. + /// + /// You MUST NOT attempt to pass a QueryInterface pointer into a + /// std::shared_ptr yourself; that will result in double-delete memory + /// errors. You must always call QueryInterfaceSharedPtr for a reference- + /// counting pointer to an interface. + /// + /// \return A reference-counting pointer to the specified interface. This + /// will keep the interface valid and the plugin instance alive, even if + /// all Plugin objects that refer to this plugin instance are destructed. + public: template + std::shared_ptr QueryInterfaceSharedPtr(); + + /// \brief Same as QueryInterfaceSharedPtr(), but it returns a + /// std::shared_ptr to a const-qualified Interface. + public: template + std::shared_ptr QueryInterfaceSharedPtr() const; + + /// \brief Checks if this Plugin has the specified type of interface. + /// \return Returns true if this Plugin has the specified type of + /// interface, and false otherwise. + public: template + bool HasInterface() const; + + /// \brief Checks if this Plugin has the specified type of interface. + /// + /// By default, we expect you to pass in a demangled version of the + /// interface name. If you want to use a mangled version of the name, + /// set the `demangled` argument to false. + /// + /// \param[in] _interfaceName The name of the desired interface, as a + /// std::string. Note that this expects the name to be mangled. + /// \param[in] _demangled If _interfaceName is demangled, set this to + /// true. If you are instead using the raw mangled name that gets provided + /// by typeid(T).name(), then set _demangled to false. + /// \return Returns true if this Plugin has the specified type of + /// interface, and false otherwise. + public: bool HasInterface(const std::string &_interfaceName, + const bool _demangled = true) const; + + /// \brief Gets the name of this Plugin. + /// \return A pointer to the name of this Plugin, or nullptr if this + /// Plugin is not instantiated. + public: const std::string *Name() const; + + // -------------------- Private API ----------------------- + + template friend class TemplatePluginPtr; + template friend class SpecializedPlugin; + template friend class detail::ComposePlugin; + template friend class detail::SelectSpecializers; + friend class EnablePluginFromThis; + friend class WeakPluginPtr; + + /// \brief Default constructor. This is kept protected to discourage users + /// from instantiating them directly. They should instead only be + /// retrieved as a PluginPtr from the plugin Loader. + protected: Plugin(); + + /// \brief Type-agnostic retriever for interfaces + private: void *PrivateQueryInterface( + const std::string &_interfaceName) const; + + /// \brief Copy the plugin instance from another Plugin object + private: void PrivateCopyPluginInstance(const Plugin &_other) const; + + /// \brief Copy an existing plugin instance into this plugin + /// \param[in] _info + /// Pointer to the Info for this plugin + /// \param[in] _instancePtr + /// Pointer to an already-existing abstract plugin instance pointer + private: void PrivateCopyPluginInstance( + const ConstInfoPtr &_info, + const std::shared_ptr &_instancePtr) const; + + /// \brief Create a new plugin instance based on the info provided + /// \param[in] _info + /// Pointer to the Info for this plugin + /// \param[in] _dlHandlePtr + /// Reference counter for the dl handle of this Plugin + private: void PrivateCreatePluginInstance( + const ConstInfoPtr &_info, + const std::shared_ptr &_dlHandlePtr) const; + + /// \brief Get a reference to the abstract instance being managed by this + /// wrapper + private: const std::shared_ptr &PrivateGetInstancePtr() const; + + /// \brief Get a reference to the Info being used by this wrapper + private: const ConstInfoPtr &PrivateGetInfoPtr() const; + + /// \brief The InterfaceMap type needs to get used in several places, like + /// Plugin::Implementation and SpecializedPlugin. We make the typedef + /// public so that those other classes can use it without needing to be + /// friends of Plugin. End-users should not have any need for this + /// typedef. + public: using InterfaceMap = std::map; + + /// \brief Get or create an iterator to the std::map that holds pointers + /// to the various interfaces provided by this plugin instance. + private: InterfaceMap::iterator PrivateGetOrCreateIterator( + const std::string &_interfaceName); + + class Implementation; + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + /// \brief PIMPL pointer to the implementation of this class. + private: const std::unique_ptr dataPtr; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + + /// \brief Virtual destructor + public: virtual ~Plugin(); + }; + } +} + +#include "gz/plugin/detail/Plugin.hh" + +#endif diff --git a/core/include/gz/plugin/PluginPtr.hh b/core/include/gz/plugin/PluginPtr.hh new file mode 100644 index 00000000..51d547c6 --- /dev/null +++ b/core/include/gz/plugin/PluginPtr.hh @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_PLUGINPTR_HH_ +#define GZ_PLUGIN_PLUGINPTR_HH_ + +#include +#include +#include + +#include + +namespace ignition +{ + namespace plugin + { + // Forward declarations + namespace detail { template class ComposePlugin; } + + /// \brief This class manages the lifecycle of a plugin instance. It can + /// receive a plugin instance from the ignition::plugin::Loader class + /// or by copy-construction or assignment from another PluginPtr instance. + /// + /// This class behaves similarly to a std::shared_ptr where multiple + /// PluginPtr objects can share a single plugin instance, and the plugin + /// instance will not be deleted until all PluginPtr objects that refer to + /// it are either destroyed, cleared, or begin referring to a different + /// plugin instance. + /// + /// A PluginPtr object can be "cast" to a SpecializedPluginPtr object by + /// simply using the copy/move constructor or assignment operator of a + /// SpecializedPluginPtr object. Note that this "cast" does have a small + /// amount of overhead associated with it, but it may result in huge savings + /// after initialization is finished if you frequently access the interfaces + /// that the SpecializedPluginPtr is specialized for. + template + class TemplatePluginPtr final + { + /// \brief Destructor. Deletes this PluginPtr's reference to the plugin + /// instance. Once all PluginPtrs that refer to a plugin instance are + /// deleted, the plugin will also be deleted. + public: ~TemplatePluginPtr() = default; + + /// \brief Default constructor. Creates a PluginPtr object that does not + /// point to any plugin instance. IsEmpty() will return true until a + /// plugin instance is provided. + public: TemplatePluginPtr(); + + /// \brief Copy constructor. This PluginPtr will now point at the same + /// plugin instance as _other, and they will share ownership. + /// \param[in] _other Pointer to plugin being copied. + public: TemplatePluginPtr(const TemplatePluginPtr &_other); + + /// \brief Move constructor. This PluginPtr will take ownership of the + /// plugin instance held by _other. If this PluginPtr was holding an + /// instance to another plugin, that instance will be deleted if no other + /// PluginPtr is referencing which is being moved. + /// \param[in] _other Plugin being moved. + public: TemplatePluginPtr(TemplatePluginPtr &&_other); + + /// \brief Casting constructor. This PluginPtr will now point at the same + /// plugin instance as _other, and they will share ownership. This + /// essentially allows casting between PluginPtrs that are holding + /// different types of plugin wrappers (for example, you can cast a + /// generic PluginPtr to any SpecializedPluginPtr type, or you can cast + /// between different types of specializations). + /// \param[in] _other Pointer to plugin being casted, which is of a + /// different type. + public: template + TemplatePluginPtr( + const TemplatePluginPtr &_other); + + /// \brief Copy assignment operator. This PluginPtr will now point at the + /// same plugin instance as _other, and they will share ownership. If this + /// PluginPtr was holding an instance to another plugin, that instance + /// will be deleted if no other PluginPtr is referencing it. + /// \param[in] _other Pointer to plugin being copied. + public: TemplatePluginPtr &operator =(const TemplatePluginPtr &_other); + + /// \brief Casting operator. This PluginPtr will now point at the same + /// plugin instance as _other, and they will share ownership. This + /// essentially allows casting between PluginPtrs that are holding + /// different types of plugin wrappers. + /// \param[in] _other Pointer to plugin being casted, which is of a + /// different type. + /// \return A reference to this object. + public: template + TemplatePluginPtr &operator =( + const TemplatePluginPtr &_other); + + /// \brief Move assignment operator. This PluginPtr will take ownership + /// of the plugin instance held by _other. If this PluginPtr was holding + /// an instance to another plugin, that instance will be deleted if no + /// other PluginPtr is referencing it. + /// \param[in] _other Plugin being moved. + /// \return A reference to this object. + public: TemplatePluginPtr &operator=(TemplatePluginPtr &&_other); + + /// \brief nullptr assignment operator. Same as calling Clear() + /// \param[in] _null nullptr object. + /// \return A reference to this object. + public: TemplatePluginPtr &operator=(std::nullptr_t _null); + + /// \brief Access the wrapper for the plugin instance and call one of its + /// member functions. + /// \return The ability to call a member function on the underlying Plugin + /// object. + public: PluginType *operator->() const; + + /// \brief Get a reference to the wrapper for the plugin instance that is + /// being managed by this PluginPtr. + /// \return A reference to the underlying Plugin object. + public: PluginType &operator*() const; + + /// \brief Comparison operator. + /// \param[in] _other Plugin to compare to. + /// \returns True if this Plugin is holding the same plugin + /// instance as _other, otherwise returns false. + public: bool operator ==(const TemplatePluginPtr &_other) const; + + /// \brief Comparison operator. + /// \param[in] _other Plugin to compare to. + /// \returns True if the pointer value of the plugin instance held + /// by this PluginPtr is less than the pointer value of the instance held + /// by _other. + public: bool operator <(const TemplatePluginPtr &_other) const; + + /// \brief Comparison operator. + /// \param[in] _other Plugin to compare to. + /// \returns True if the pointer value of the plugin instance held + /// by this PluginPtr is greater than the pointer value of the instance + /// held by _other. + public: bool operator >(const TemplatePluginPtr &_other) const; + + /// \brief Comparison operator. + /// \param[in] _other Plugin to compare to. + /// \returns True if the pointer instance held by this PluginPtr is + /// different from the pointer instance held by _other. + public: bool operator !=(const TemplatePluginPtr &_other) const; + + /// \brief Comparison operator. + /// \param[in] _other Plugin to compare to. + /// \returns True if the value of the pointer instance held by this + /// PluginPtr is less than or equal to the value of the pointer instance + /// held by _other. + public: bool operator <=(const TemplatePluginPtr &_other) const; + + /// \brief Comparison operator. + /// \param[in] _other Plugin to compare to. + /// \returns True if the value of the pointer instance held by this + /// PluginPtr is greater than or equal to the value of the pointer + /// instance held by _other. + public: bool operator >=(const TemplatePluginPtr &_other) const; + + /// \brief Produces a hash for the plugin instance that this PluginPtr is + /// holding. This function allows PluginPtr instances to be used as values + /// in a std::unordered_set or keys in a + /// std::unordered_map. Using this function directly should + /// not normally be necessary. + /// \return A hash of the underlying pointer object. + public: std::size_t Hash() const; + + /// \brief Check whether this is pointing at a valid plugin. + /// \returns False if this PluginPtr is pointing at a valid plugin + /// instance. If it is instead pointing at a nullptr, this returns true. + public: bool IsEmpty() const; + + /// \brief Implicitly convert this PluginPtr to a boolean. + /// \return The opposite value of IsEmpty(). + public: operator bool() const; + + /// \brief Clears the Plugin instance from this PluginPtr. IsEmpty() will + /// return true after this is used, and none of the interfaces will be + /// available any longer. + public: void Clear(); + + /// \brief Pointer to the plugin wrapper that this PluginPtr is managing. + private: std::unique_ptr dataPtr; + + // Declare friendship + friend class Loader; + template friend class TemplatePluginPtr; + + /// \brief Private constructor. Creates a plugin instance based on the + /// Info provided. This should only be called by Loader to ensure that + /// the Info is well-formed, so we keep it private. + /// \param[in] _info An Info instance that was generated by + /// Loader. Alternatively, this can take a nullptr to create an + /// empty PluginPtr. + /// \param[in] _dlHandlePtr A reference count for the DL handle. + private: explicit TemplatePluginPtr( + const ConstInfoPtr &_info, + const std::shared_ptr &_dlHandlePtr); + }; + + /// \brief Typical usage for TemplatePluginPtr is to just hold a generic + /// Plugin type. + using PluginPtr = TemplatePluginPtr; + + /// \brief This produces a PluginPtr whose Plugin wrapper only grants access + /// to const-qualified interfaces of the plugin instance. + using ConstPluginPtr = TemplatePluginPtr; + } +} + +#include "gz/plugin/detail/PluginPtr.hh" + +#endif diff --git a/core/include/gz/plugin/SpecializedPlugin.hh b/core/include/gz/plugin/SpecializedPlugin.hh new file mode 100644 index 00000000..71fbcced --- /dev/null +++ b/core/include/gz/plugin/SpecializedPlugin.hh @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_SPECIALIZEDPLUGIN_HH_ +#define GZ_PLUGIN_SPECIALIZEDPLUGIN_HH_ + +#include +#include "gz/plugin/Plugin.hh" + +namespace ignition +{ + namespace plugin + { + // Forward declarations + namespace detail { + template class ComposePlugin; + template class SelectSpecializers; + } + + // Forward declaration of the variadic template SpecializedPlugin class. + template + class SpecializedPlugin; + + /// \brief This class allows Plugin instances to have high-speed access to + /// interfaces that can be anticipated at compile time. The plugin does + /// not have to actually offer the specialized interface in order to get + /// this performance improvement. This template is variadic, so it can + /// support arbitrarily many interfaces. + /// + /// Usage example: + /// + /// \code + /// using MySpecialPluginPtr = SpecializedPluginPtr< + /// MyInterface1, FooInterface, MyInterface2, BarInterface>; + /// + /// MySpecialPluginPtr plugin = loader->Instantiate(pluginName); + /// \endcode + /// + /// Then, calling the function + /// + /// \code + /// plugin->QueryInterface(); + /// \endcode + /// + /// will have extremely high-speed associated with it. It will provide + /// direct access to the the `FooInterface*` of `plugin`. If `plugin` does + /// not actually offer `FooInterface`, then it will return a nullptr, still + /// at extremely high speed. + template + class SpecializedPlugin : public virtual Plugin + { + // -------------------- Public API --------------------- + + // Inherit function overloads + public: using Plugin::QueryInterface; + public: using Plugin::QueryInterfaceSharedPtr; + public: using Plugin::HasInterface; + + // Documentation inherited + public: template + Interface *QueryInterface(); + + // Documentation inherited + public: template + const Interface *QueryInterface() const; + + // Documentation inherited + public: template + std::shared_ptr QueryInterfaceSharedPtr(); + + // Documentation inherited + public: template + std::shared_ptr QueryInterfaceSharedPtr() const; + + // Documentation inherited + public: template + bool HasInterface() const; + + + // -------------------- Private API --------------------- + + // Declare friendship + template friend class SpecializedPlugin; + template friend class detail::ComposePlugin; + template friend class detail::SelectSpecializers; + template friend class TemplatePluginPtr; + + /// \brief type is an empty placeholder class which is used by the private + /// member functions to provide two overloads: a high-performance one for + /// the specialized interface, and a normal-performance one for all other + /// Interface types. + private: template struct type { }; + + /// \brief Delegate the function to the standard Plugin method + /// \param[in] _type Empty object meant to guide the compiler to pick the + /// desired implementation. + /// \return Pointer to the intergace + private: template + Interface *PrivateQueryInterface(type); + + /// \brief Use a high-speed accessor to provide this specialized interface + /// \param[in] _type Empty object meant to guide the compiler to pick the + /// desired implementation. + /// \return Pointer to the specialized intergace + private: SpecInterface *PrivateQueryInterface(type); + + /// \brief Delegate the function to the standard Plugin method + /// \param[in] _type Empty object meant to guide the compiler to pick the + /// desired implementation. + /// \return Pointer to the specialized intergace + private: template + const Interface *PrivateQueryInterface(type) const; + + /// \brief Use a high-speed accessor to provide this specialized interface + /// \param[in] _type Empty object meant to guide the compiler to pick the + /// desired implementation. + /// \return Pointer to the specialized intergace + private: const SpecInterface *PrivateQueryInterface( + type) const; + + /// \brief Delegate the function to the standard PluginPtr method + /// \param[in] _type Empty object meant to guide the compiler to pick the + /// desired implementation. + /// \return True if the interface is present. + private: template + bool PrivateHasInterface(type) const; + + /// \brief Use a high-speed accessor to check this specialized interface + /// \param[in] _type Empty object meant to guide the compiler to pick the + /// desired implementation. + /// \return True if the interface is present. + private: bool PrivateHasInterface(type) const; + + // Dev note (MXG): The privateSpecializedInterfaceIterator object must be + // available to the user during their compile time, so it cannot be hidden + // using PIMPL. The iterator is const because it must always point to the + // same entry throughout its entire lifecycle. + /// \brief Iterator that points to the entry of the specialized interface + private: + const Plugin::InterfaceMap::iterator privateSpecializedInterfaceIterator; + + /// \brief Default constructor + protected: SpecializedPlugin(); + }; + } +} + +#include "gz/plugin/detail/SpecializedPlugin.hh" + +#endif diff --git a/core/include/gz/plugin/SpecializedPluginPtr.hh b/core/include/gz/plugin/SpecializedPluginPtr.hh new file mode 100644 index 00000000..6e9ba354 --- /dev/null +++ b/core/include/gz/plugin/SpecializedPluginPtr.hh @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_SPECIALIZEDPLUGINPTR_HH_ +#define GZ_PLUGIN_SPECIALIZEDPLUGINPTR_HH_ + +#include "gz/plugin/PluginPtr.hh" +#include "gz/plugin/SpecializedPlugin.hh" + +namespace ignition +{ + namespace plugin + { + /// \brief This alias allows PluginPtr instances to have high-speed access + /// to interfaces that can be anticipated at compile time. The plugin does + /// not have to actually offer the specialized interface in order to get + /// this performance improvement. This template is variadic, so it can + /// support arbitrarily many interfaces. + /// + /// Usage example: + /// + /// Suppose you want to instantiate a plugin that might (or might not) have + /// some combination of four interfaces which are known at compile time: + /// `MyInterface1`, `FooInterface`, `MyInterface2`, and `BarInterface`. You + /// can use SpecializedPluginPtr as shown here: + /// + /// \code + /// using MySpecialPluginPtr = SpecializedPluginPtr< + /// MyInterface1, FooInterface, MyInterface2, BarInterface>; + /// + /// MySpecialPluginPtr plugin = loader->Instantiate(pluginName); + /// \endcode + /// + /// Then, calling the function + /// + /// \code + /// plugin->QueryInterface(); + /// \endcode + /// + /// will have extremely high speed associated with it. It will provide + /// direct access to the the `FooInterface*` of `plugin`. If `plugin` does + /// not actually offer `FooInterface`, then it will return a nullptr, still + /// at extremely high speed. + /// + /// This same rule also applies to `MyInterface1`, `MyInterface2`, and + /// `BarInterface`, because those interfaces were also provided to + /// SpecializedPluginPtr<...> at compile time. + template + using SpecializedPluginPtr = + TemplatePluginPtr< SpecializedPlugin >; + + /// \brief This alias creates a specialized PluginPtr whose interfaces are + /// all const-qualified. + template + using ConstSpecializedPluginPtr = + TemplatePluginPtr< const SpecializedPlugin >; + } +} + +#endif diff --git a/core/include/gz/plugin/WeakPluginPtr.hh b/core/include/gz/plugin/WeakPluginPtr.hh new file mode 100644 index 00000000..5968d71b --- /dev/null +++ b/core/include/gz/plugin/WeakPluginPtr.hh @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GZ_PLUGIN_WEAKPLUGINPTR_HH_ +#define GZ_PLUGIN_WEAKPLUGINPTR_HH_ + +#include + +#include + +namespace ignition +{ + namespace plugin + { + /// \brief WeakPluginPtr is a non-reference-holding smart pointer for a + /// Plugin. WeakPluginPtr is analogous to std::weak_ptr where PluginPtr is + /// analogous to std::shared_ptr. + /// + /// Use Lock() to receive a reference-holding PluginPtr that points to the + /// Plugin that this WeakPluginPtr is meant to reference. + /// + /// If the Plugin is deleted while this WeakPluginPtr is referring to it, + /// then Lock() will return an empty PluginPtr, and IsExpired() will return + /// true. + class IGNITION_PLUGIN_VISIBLE WeakPluginPtr + { + /// \brief Default constructor + public: WeakPluginPtr(); + + /// \brief Copy constructor + /// \param[in] _other + /// Another WeakPluginPtr to copy + public: WeakPluginPtr(const WeakPluginPtr &_other); + + /// \brief Move constructor + /// \param[in] _other + /// Another WeakPluginPtr to move from + public: WeakPluginPtr(WeakPluginPtr &&_other); + + /// \brief Construct from a live PluginPtr + /// \param[in] _ptr + /// A live PluginPtr to refer to. As long as the Plugin instance + /// continues to be held by PluginPtr containers, you can use Lock() to + /// retrieve a reference to it. + // cppcheck-suppress noExplicitConstructor + public: WeakPluginPtr(const PluginPtr &_ptr); + + /// \brief Copy assignment operator + /// \param[in] _other + /// Another WeakPluginPtr to copy + /// \return reference to this + public: WeakPluginPtr &operator=(const WeakPluginPtr &_other); + + /// \brief Move assignment operator + /// \param[in] _other + /// Another WeakPluginPtr to move from + /// \return reference to this + public: WeakPluginPtr &operator=(WeakPluginPtr &&_other); + + /// \brief Assign from a live PluginPtr + /// \param[in] _ptr + /// A live PluginPtr to refer to. As long as the Plugin instance + /// continues to be held by PluginPtr containers, you can use Lock() to + /// retrieve a reference to it. + public: WeakPluginPtr &operator=(const PluginPtr &_ptr); + + /// \brief Retrieve the PluginPtr that this WeakPluginPtr refers to, if it + /// is still available. Otherwise, retrieve an empty PluginPtr. + /// \return The PluginPtr that this WeakPluginPtr refers to. + public: PluginPtr Lock() const; + + /// \brief Check whether the referenced Plugin has already expired. + /// \return true if this PluginPtr is expired, false otherwise. + public: bool IsExpired() const; + + /// \brief Destructor + public: ~WeakPluginPtr(); + + private: class Implementation; + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + /// \brief PIMPL pointer to the implementation of this class + private: std::unique_ptr pimpl; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + }; + } +} + +#endif diff --git a/core/include/ignition/plugin/config.hh.in b/core/include/gz/plugin/config.hh.in similarity index 100% rename from core/include/ignition/plugin/config.hh.in rename to core/include/gz/plugin/config.hh.in diff --git a/core/include/gz/plugin/detail/Factory.hh b/core/include/gz/plugin/detail/Factory.hh new file mode 100644 index 00000000..73e5716a --- /dev/null +++ b/core/include/gz/plugin/detail/Factory.hh @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2018 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef GZ_PLUGIN_DETAIL_FACTORY_HH_ +#define GZ_PLUGIN_DETAIL_FACTORY_HH_ + +#include +#include + +#include + +#include + +namespace ignition +{ + namespace plugin + { + namespace detail + { + /// \brief This base class gets mixed in with a Product so that the + /// Product can keep track of the factory that produced it. This allows + /// the factory's library to remain loaded (so that the Product's symbols + /// remain available to the application). Without this, the shared library + /// might get unloaded while the Product is still alive, and then the + /// application will experience a segmentation fault as soon as it tries + /// to do anything with the Product (including deleting it). + class IGNITION_PLUGIN_VISIBLE FactoryCounter + { + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + /// \brief A reference to the factory that created this product + private: std::shared_ptr factoryPluginInstancePtr; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + + /// \brief A special destructor that ensures the shared library remains + /// loaded throughout the destruction process of this product. + public: virtual ~FactoryCounter(); + + // friendship declaration + template friend class ignition::plugin::Factory; + template friend class ignition::plugin::ProductDeleter; + }; + } + + template + class ProductDeleter + { + /// \brief This is a unary function for deleting product pointers. It + /// keeps the factory reference alive while the product is being deleted, + /// and then cleans up the factory reference immediately afterwards. + /// + /// This is the recommended method for deleting product pointers. + public: void operator()(Interface *_ptr) + { + detail::FactoryCounter *counter = + dynamic_cast(_ptr); + + std::shared_ptr factoryPluginInstancePtr; + if (counter) + { + // Hold onto the factory instance pointer while the product completes + // its destruction. + // + // At the same time, clear out the product's reference to its factory + // so that it knows that it's being deleted by a ProductDeleter. + // Otherwise, it will intentionally leak its factory reference to + // avoid causing a segmentation fault in the application. + factoryPluginInstancePtr.swap(counter->factoryPluginInstancePtr); + } + + delete _ptr; + } + }; + + template + auto Factory::Construct(Args&&... _args) + -> ProductPtrType + { + return ProductPtrType(this->ImplConstruct(std::forward(_args)...)); + } + + /// \brief Producing provides the implementation of Factory for a specific + /// derivative of Factory's Interface type. That derivative is called + /// Product, which must be a fully-defined class that implements Interface. + /// + /// The mechanism of this class ensures that as long as the Factory output + /// object is alive, the plugin library that it depends on will remain + /// safely loaded. + template + // cppcheck-suppress syntaxError + template + class Factory::Producing + : public Factory + { + /// \brief This class mixes the product implementation with the factory + /// counter so that the factory remains alive and the library remains + /// loaded throughout the lifecycle of the product. + /// + /// Dev note(MXG): FactoryCounter is inherited first so that its + /// destructor gets called last. In case this product is not deleted by a + /// ProductDeleter, having the FactoryCounter destruct last will minimize + /// the amount of time between handing off its shared_ptr to the + /// lostProductManager and the time that the call stack leaves the + /// destructor of this symbol entirely. This makes it less risky to call + /// CleanupLostProducts() in a multi-threaded application. Combined with + /// giving CleanupLostProducts() a brief waiting period, this should allow + /// even completely reckless multi-threaded applications to be able to + /// cleanup its lost products safely. + class ProductWithFactoryCounter + : public detail::FactoryCounter, + public Product + { + /// \brief Forwarding constructor + public: ProductWithFactoryCounter(Args&&... _args) + : Product(std::forward(_args)...) + { + // Do nothing + } + }; + + // Documentation inherited + private: Interface *ImplConstruct(Args&&... _args) override + { + auto *product = + new ProductWithFactoryCounter(std::forward(_args)...); + + product->factoryPluginInstancePtr = this->PluginInstancePtrFromThis(); + + return product; + } + }; + } +} + +#endif diff --git a/core/include/gz/plugin/detail/Plugin.hh b/core/include/gz/plugin/detail/Plugin.hh new file mode 100644 index 00000000..6e1b8070 --- /dev/null +++ b/core/include/gz/plugin/detail/Plugin.hh @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_DETAIL_PLUGIN_HH_ +#define GZ_PLUGIN_DETAIL_PLUGIN_HH_ + +#include +#include +#include + +namespace ignition +{ + namespace plugin + { + ////////////////////////////////////////////////// + template + Interface *Plugin::QueryInterface() + { + return static_cast( + this->PrivateQueryInterface(typeid(Interface).name())); + } + + ////////////////////////////////////////////////// + template + const Interface *Plugin::QueryInterface() const + { + return static_cast( + this->PrivateQueryInterface(typeid(Interface).name())); + } + + ////////////////////////////////////////////////// + template + std::shared_ptr Plugin::QueryInterfaceSharedPtr() + { + Interface *ptr = this->QueryInterface(); + if (ptr) + return std::shared_ptr(this->PrivateGetInstancePtr(), ptr); + + return nullptr; + } + + ////////////////////////////////////////////////// + template + std::shared_ptr Plugin::QueryInterfaceSharedPtr() const + { + const Interface *ptr = this->QueryInterface(); + if (ptr) + { + return std::shared_ptr( + this->PrivateGetInstancePtr(), ptr); + } + + return nullptr; + } + + ////////////////////////////////////////////////// + template + bool Plugin::HasInterface() const + { + return this->HasInterface(typeid(Interface).name(), false); + } + } +} + +#endif diff --git a/core/include/gz/plugin/detail/PluginPtr.hh b/core/include/gz/plugin/detail/PluginPtr.hh new file mode 100644 index 00000000..2372928d --- /dev/null +++ b/core/include/gz/plugin/detail/PluginPtr.hh @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_DETAIL_PLUGINPTR_HH_ +#define GZ_PLUGIN_DETAIL_PLUGINPTR_HH_ + +#include +#include +#include +#include + +namespace ignition +{ + namespace plugin + { + ////////////////////////////////////////////////// + template + TemplatePluginPtr::TemplatePluginPtr() + : dataPtr(new PluginType) + { + // Do nothing + } + + ////////////////////////////////////////////////// + template + TemplatePluginPtr::TemplatePluginPtr( + const TemplatePluginPtr &_other) + : dataPtr(new PluginType) + { + this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); + } + + ////////////////////////////////////////////////// + template + // the following is a false positive with cppcheck 1.82 fixed in 1.83 + // cppcheck-suppress syntaxError + template + TemplatePluginPtr::TemplatePluginPtr( + const TemplatePluginPtr &_other) + : dataPtr(new PluginType) + { + static_assert(ConstCompatible::value, + "The requested PluginPtr cast would discard const qualifiers"); + this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); + } + + ////////////////////////////////////////////////// + template + TemplatePluginPtr& TemplatePluginPtr::operator =( + const TemplatePluginPtr &_other) + { + this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); + return *this; + } + + ////////////////////////////////////////////////// + template + template + TemplatePluginPtr& TemplatePluginPtr::operator =( + const TemplatePluginPtr &_other) + { + static_assert(ConstCompatible::value, + "The requested PluginPtr cast would discard const qualifiers"); + this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); + return *this; + } + + ////////////////////////////////////////////////// + template + TemplatePluginPtr::TemplatePluginPtr( + TemplatePluginPtr &&_other) + : dataPtr(std::move(_other.dataPtr)) + { + // Do nothing + } + + ////////////////////////////////////////////////// + template + TemplatePluginPtr& TemplatePluginPtr::operator =( + TemplatePluginPtr &&_other) + { + this->dataPtr = std::move(_other.dataPtr); + return *this; + } + + ////////////////////////////////////////////////// + template + TemplatePluginPtr& TemplatePluginPtr::operator =( + std::nullptr_t) + { + this->Clear(); + return *this; + } + + ////////////////////////////////////////////////// + template + PluginType* TemplatePluginPtr::operator ->() const + { + return dataPtr.get(); + } + + ////////////////////////////////////////////////// + template + PluginType& TemplatePluginPtr::operator *() const + { + return (*dataPtr); + } + + ////////////////////////////////////////////////// + #define DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR(op)\ + template \ + bool TemplatePluginPtr::operator op (\ + const TemplatePluginPtr &_other) const\ + {\ + return (this->dataPtr->PrivateGetInstancePtr() op \ + _other.dataPtr->PrivateGetInstancePtr() );\ + } + + DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( == ) // NOLINT + DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( < ) // NOLINT + DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( > ) // NOLINT + DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( != ) // NOLINT + DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( <= ) // NOLINT + DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( >= ) // NOLINT + + ////////////////////////////////////////////////// + template + std::size_t TemplatePluginPtr::Hash() const + { + return std::hash< std::shared_ptr >()( + this->dataPtr->PrivateGetInstancePtr()); + } + + ////////////////////////////////////////////////// + template + bool TemplatePluginPtr::IsEmpty() const + { + return (nullptr == this->dataPtr->PrivateGetInstancePtr()); + } + + ////////////////////////////////////////////////// + template + TemplatePluginPtr::operator bool() const + { + return !this->IsEmpty(); + } + + ////////////////////////////////////////////////// + template + void TemplatePluginPtr::Clear() + { + this->dataPtr->PrivateCreatePluginInstance(nullptr, nullptr); + } + + ////////////////////////////////////////////////// + template + TemplatePluginPtr::TemplatePluginPtr( + const ConstInfoPtr &_info, + const std::shared_ptr &_dlHandlePtr) + : dataPtr(new PluginType) + { + dataPtr->PrivateCreatePluginInstance(_info, _dlHandlePtr); + } + } +} + +// Note that opening up namespace std is legal here because we are specializing +// a templated structure from the STL, which is permitted (and even encouraged). +namespace std +{ + /// \brief Template specialization that provides a hash function for PluginPtr + /// so that it can easily be used in STL objects like std::unordered_set and + /// std::unordered_map + template + struct hash> + { + size_t operator()( + const ignition::plugin::TemplatePluginPtr &ptr) const + { + return ptr.Hash(); + } + }; +} + +#endif diff --git a/core/include/gz/plugin/detail/SpecializedPlugin.hh b/core/include/gz/plugin/detail/SpecializedPlugin.hh new file mode 100644 index 00000000..78427252 --- /dev/null +++ b/core/include/gz/plugin/detail/SpecializedPlugin.hh @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_DETAIL_SPECIALIZEDPLUGIN_HH_ +#define GZ_PLUGIN_DETAIL_SPECIALIZEDPLUGIN_HH_ + +#include +#include "gz/plugin/SpecializedPlugin.hh" + +// This preprocessor token should only be used by the unittest that is +// responsible for checking that the specialized routines are being used to +// access specialized plugin interfaces. +#ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS +bool usedSpecializedInterfaceAccess; +#endif + + +namespace ignition +{ + namespace plugin + { + ///////////////////////////////////////////////// + template + // the following is a false positive with cppcheck 1.82 fixed in 1.83 + // cppcheck-suppress syntaxError + template + Interface *SpecializedPlugin::QueryInterface() + { + return this->PrivateQueryInterface(type()); + } + + ///////////////////////////////////////////////// + template + template + const Interface *SpecializedPlugin::QueryInterface() const + { + return this->PrivateQueryInterface(type()); + } + + ///////////////////////////////////////////////// + template + template + std::shared_ptr + SpecializedPlugin::QueryInterfaceSharedPtr() + { + Interface *ptr = this->QueryInterface(); + if (ptr) + return std::shared_ptr(this->PrivateGetInstancePtr(), ptr); + + return nullptr; + } + + ///////////////////////////////////////////////// + template + template + std::shared_ptr + SpecializedPlugin::QueryInterfaceSharedPtr() const + { + const Interface *ptr = this->QueryInterface(); + if (ptr) + return std::shared_ptr(this->PrivateGetInstancePtr(), ptr); + + return nullptr; + } + + ///////////////////////////////////////////////// + template + template + bool SpecializedPlugin::HasInterface() const + { + return this->PrivateHasInterface(type()); + } + + ///////////////////////////////////////////////// + // This function is necessary for compilation, but COMPOSEPLUGIN_DISPATCH + // always seems to call the base Plugin class instead of this. + // So we will exclude it from code coverage. + // LCOV_EXCL_START + template + template + Interface *SpecializedPlugin::PrivateQueryInterface( + type) + { + return this->Plugin::QueryInterface(); + } + // LCOV_EXCL_STOP + + ///////////////////////////////////////////////// + template + SpecInterface *SpecializedPlugin::PrivateQueryInterface( + type) + { + #ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS + usedSpecializedInterfaceAccess = true; + #endif + return static_cast( + this->privateSpecializedInterfaceIterator->second); + } + + ///////////////////////////////////////////////// + template + template + const Interface *SpecializedPlugin:: + PrivateQueryInterface(type) const + { + return this->Plugin::QueryInterface(); + } + + ///////////////////////////////////////////////// + template + const SpecInterface *SpecializedPlugin:: + PrivateQueryInterface(type) const + { + #ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS + usedSpecializedInterfaceAccess = true; + #endif + return static_cast( + this->privateSpecializedInterfaceIterator->second); + } + + ///////////////////////////////////////////////// + template + template + bool SpecializedPlugin::PrivateHasInterface( + type) const + { + return this->Plugin::HasInterface(); + } + + ///////////////////////////////////////////////// + template + bool SpecializedPlugin::PrivateHasInterface( + type) const + { + #ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS + usedSpecializedInterfaceAccess = true; + #endif + return (nullptr != this->privateSpecializedInterfaceIterator->second); + } + + ///////////////////////////////////////////////// + template + SpecializedPlugin::SpecializedPlugin() + : privateSpecializedInterfaceIterator( + this->PrivateGetOrCreateIterator( + typeid(SpecInterface).name())) + { + // Do nothing + } + + namespace detail + { + /// \brief This template provides an implementation of + /// SelectSpecializerIfAvailable by having two template specializations + /// to choose between at compile time. + /// + /// If specialized is true, then this will provide the specializer for + /// Interface as `Specializer`. + template + struct SelectSpecalizerIfAvailableImpl + { + using Specializer = SpecializedPlugin; + }; + + /// \brief This template specialization will be invoked when + /// Specialization is not specialized for Interface, and therefore return + /// the generic Plugin type. + template + struct SelectSpecalizerIfAvailableImpl + { + using Specializer = Plugin; + }; + + /// \brief If Specialization contains a leaf specializer for Interface, + /// i.e. SpecializedPlugin, then this will provide that type + /// under the name `Specializer`. Otherwise, this will simply provide the + /// generic Plugin type. + template + struct SelectSpecalizerIfAvailable + { + using Specializer = typename SelectSpecalizerIfAvailableImpl, Specialization>::value + >::Specializer; + }; + + template + class SelectSpecializers : public virtual Base + { + /// \brief Default destructor + public: virtual ~SelectSpecializers() = default; + + // Inherit function overloads + using Plugin::QueryInterface; + using Plugin::QueryInterfaceSharedPtr; + using Plugin::HasInterface; + + // Used for template metaprogramming + using Specialization = Base; + + /// \brief Implement functions whose only roles are to dispatch their + /// functionalities between two base classes, depending on which base is + /// specialized for the template type. This must only be called within + /// the ComposePlugin class. + /// + /// The dispatch is performed by casting this object to the type that + /// specializes for the requested Interface, if such a type is availabe + /// within its inheritance structure. Otherwise, we cast to the generic + /// Plugin type. + #define DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( \ + ReturnType, Function, Suffix, CastTo, Args) \ + public: \ + template \ + ReturnType Function Suffix \ + { \ + using Specializer = typename detail::SelectSpecalizerIfAvailable< \ + T, Specialization>::Specializer; \ + return static_cast(this)->template Function Args; \ + } + + + DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( + T*, QueryInterface, (), Specializer, ()) + + DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( + const T*, QueryInterface, () const, const Specializer, ()) + + DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( + std::shared_ptr, QueryInterfaceSharedPtr, (), Specializer, ()) + + DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( + std::shared_ptr, QueryInterfaceSharedPtr, + () const, const Specializer, ()) + + DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( + bool, HasInterface, () const, const Specializer, ()) + + // Declare friendship + template friend class ignition::plugin::SpecializedPlugin; + template friend class SelectSpecializers; + template friend class ComposePlugin; + + protected: SelectSpecializers() = default; + }; + + /// \brief ComposePlugin provides a way for a multi-specialized Plugin + /// type to find its specializations within itself each time an + /// interface-querying function is called. The macro + /// DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH accomplishes this for each + /// of the different functions by doing a compile-time check on whether + /// Base2 contains the specialization, and then picks Base1 if it does + /// not. + template + class ComposePlugin : public virtual Base1, public virtual Base2 + { + // Used for template metaprogramming + using Specialization = ComposePlugin; + + // Declare friendship + template friend class ignition::plugin::SpecializedPlugin; + template friend class SelectSpecializers; + template friend class ComposePlugin; + + protected: ComposePlugin() = default; + }; + + /// \brief This template specialization is used when Base1 and Base2 are + /// identical. It dodges a compilation error that occurs when a class + /// tries to double-inherit a direct base class. + template + class ComposePlugin + : public virtual RepeatedBase { }; // NOLINT + } + + /// \brief Construct an unbalanced binary tree of specializations by + /// convoluting SpecializedPlugin types using ComposePlugin. + template + class SpecializedPlugin : + public virtual detail::SelectSpecializers< + detail::ComposePlugin< + SpecializedPlugin, + SpecializedPlugin + > + > + { + // Declare friendship + template friend class SpecializedPlugin; + template friend class detail::ComposePlugin; + template friend class detail::SelectSpecializers; + template friend class TemplatePluginPtr; + + /// \brief Virtual destructor + public: virtual ~SpecializedPlugin() = default; + + /// \brief Default constructor + protected: SpecializedPlugin() = default; + }; + + /// \brief Allow empty specializations of SpecializedPlugin + template <> + class SpecializedPlugin<> : public virtual Plugin { }; // NOLINT + } +} + +#endif diff --git a/core/include/gz/plugin/detail/utility.hh b/core/include/gz/plugin/detail/utility.hh new file mode 100644 index 00000000..af24e17d --- /dev/null +++ b/core/include/gz/plugin/detail/utility.hh @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_DETAIL_UTILITY_HH_ +#define GZ_PLUGIN_DETAIL_UTILITY_HH_ + + +#include + +namespace ignition +{ + namespace plugin + { + namespace detail + { + ////////////////////////////////////////////////// + template + struct ConstCompatible : std::true_type + { + }; + + ////////////////////////////////////////////////// + template + struct ConstCompatible + : std::integral_constant::value> + { + }; + } + } +} + +#endif diff --git a/core/include/gz/plugin/utility.hh b/core/include/gz/plugin/utility.hh new file mode 100644 index 00000000..867f9e1f --- /dev/null +++ b/core/include/gz/plugin/utility.hh @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_UTILITY_HH_ +#define GZ_PLUGIN_UTILITY_HH_ + +#include + +#include +#include + +namespace ignition +{ + namespace plugin + { + ///////////////////////////////////////////////// + /// \brief Contains a static constexpr field named `value` which will be + /// true if the type `From` has a const-quality less than or equal to the + /// type `To`. + /// + /// The following expressions will return true: + /// + /// \code + /// ConstCompatible::value + /// ConstCompatible::value + /// \endcode + /// + /// The following expression will return false: + /// + /// \code + /// ConstCompatible::value + /// \endcode + /// + template + using ConstCompatible = detail::ConstCompatible; + + + ///////////////////////////////////////////////// + /// \brief Demangle the ABI typeinfo name of a symbol into a human-readable + /// version. + /// \param[in] _symbol + /// Pass in the result of typeid(T).name() + /// \return The demangled (human-readable) version of the symbol name + std::string IGNITION_PLUGIN_VISIBLE DemangleSymbol( + const std::string &_symbol); + } +} + +#endif diff --git a/core/include/ignition/plugin.hh b/core/include/ignition/plugin.hh new file mode 100644 index 00000000..98c5f190 --- /dev/null +++ b/core/include/ignition/plugin.hh @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include diff --git a/core/include/ignition/plugin/EnablePluginFromThis.hh b/core/include/ignition/plugin/EnablePluginFromThis.hh index 01ad28a9..7825a45e 100644 --- a/core/include/ignition/plugin/EnablePluginFromThis.hh +++ b/core/include/ignition/plugin/EnablePluginFromThis.hh @@ -15,72 +15,4 @@ * */ -#ifndef IGNITION_PLUGIN_ENABLEPLUGINFROMTHIS_HH_ -#define IGNITION_PLUGIN_ENABLEPLUGINFROMTHIS_HH_ - -#include - -#include - -namespace ignition -{ - namespace plugin - { - // Forward declaration - class Loader; - - /// \brief EnablePluginFromThis is an optional base class which can be - /// inherited by Plugin classes. When a Plugin class inherits it and that - /// Plugin class is instantiated using the Loader class, its instance will - /// be able to access the PluginPtr that manages its lifecycle. This - /// interface will also be available by calling - /// `instance->QueryInterface()` - /// - /// \remark This class is analogous to std::enable_shared_from_this - class IGNITION_PLUGIN_VISIBLE EnablePluginFromThis - { - /// \brief Default constructor - public: EnablePluginFromThis(); - - /// \brief Get a copy of the PluginPtr that manages this object. - /// \return a copy of the PluginPtr that manages this object. - public: PluginPtr PluginFromThis(); - - /// \brief Get a const-qualified copy of the PluginPtr that manages this - /// object. - /// \return a const-qualified copy of the PluginPtr that manages this - /// object. - public: ConstPluginPtr PluginFromThis() const; - - /// \brief Destructor - public: virtual ~EnablePluginFromThis(); - - /// \brief This is an abstract shared_ptr to the Plugin instance. This - /// shared_ptr will maintain a reference count on the shared library that - /// provides the plugin so that the shared library does not get unloaded - /// while this shared_ptr is still alive. - /// - /// This function is meant for advanced use-cases, such as creating - /// Factory plugins, so we keep it protected. Only use this if you know - /// what you are doing. - /// - /// \return shared_ptr to the Plugin instance. - protected: std::shared_ptr PluginInstancePtrFromThis() const; - - // Declare friendship so that the internal WeakPluginPtr can be set by - // the Loader class. - friend class Loader; - - /// \brief This function is called by the Loader class whenever a plugin - /// containing this interface gets instantiated. - private: void PrivateSetPluginFromThis(const PluginPtr &_ptr); - - private: class Implementation; - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - private: std::unique_ptr pimpl; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - }; - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/Export.hh b/core/include/ignition/plugin/Export.hh new file mode 100644 index 00000000..50c1d69e --- /dev/null +++ b/core/include/ignition/plugin/Export.hh @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include diff --git a/core/include/ignition/plugin/Factory.hh b/core/include/ignition/plugin/Factory.hh index 3cf47113..487744b3 100644 --- a/core/include/ignition/plugin/Factory.hh +++ b/core/include/ignition/plugin/Factory.hh @@ -13,136 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ -#ifndef IGNITION_PLUGIN_FACTORY_HH_ -#define IGNITION_PLUGIN_FACTORY_HH_ - -#include -#include -#include -#include - -#include - -namespace ignition -{ - namespace plugin - { - /// \brief This class provides a unary operator for safely deleting pointers - /// to plugin factory products with the type `Interface`. If it gets passed - /// an `Interface` pointer that is not pointing to a factory plugin product, - /// then this just performs a normal delete. - template class ProductDeleter; - - /// \brief ProductPtr is a derivative of std::unique_ptr that can safely - /// manage the products that come out of a plugin factory. It is strongly - /// recommended that factory products use a ProductPtr to manage the - /// lifecycle of a factory product. - /// - /// If you MUST release a factory product pointer, then it should at least - /// be passed to a ProductDeleter to be deleted at the end of its lifecycle. - /// If it is not possible to delete the product with a ProductDeleter, then - /// you will need to periodically call CleanupLostProducts() or else you - /// will experience memory leaks, and your factory plugin libraries will - /// never get unloaded. - template - using ProductPtr = - std::unique_ptr>; - - /// \brief The Factory class defines a plugin factory that can be used by - /// the Loader class to produce products that implement an interface. - /// - /// To define the inputs and outputs of a factory, set the template - /// arguments, for example: - /// - /// \code - /// using MyFactory = Factory; - /// \endcode - /// - /// defines a factory that can produce a `std::unique_ptr` - /// given arguments of `InputType1` and `InputType2`. - /// - /// To register a factory, use the `IGNITION_ADD_FACTORY` macro, e.g.: - /// - /// \code - /// IGNITION_ADD_FACTORY(ImplementedClass, MyFactory) - /// \endcode - /// - /// where `ImplementedClass` is the name of the class that your plugin - /// library has used to implement `InterfaceClass`. - template - class Factory : public EnablePluginFromThis - { - public: using ProductPtrType = ProductPtr; - - /// \brief This function is called by Loader to construct the class that - /// implements the InterfacePtr interface. - /// \param[in] _args - /// The arguments as defined by the template parameters. - /// \return an RAII-managed reference to the interface type as defined by - /// the template parameters. - public: ProductPtrType Construct(Args&&... _args); - - /// \internal \brief This function gets implemented by Producing - /// to manufacture the product instance. - /// \param[in] _args - /// The arguments as defined by the template parameters - /// \return a raw pointer to the product - private: virtual Interface *ImplConstruct(Args&&... _args) = 0; - - /// \private This nested class is used to implement the plugin factory. - /// It is not intended for external use. - public: template - class Producing; - }; - - /// \brief Call this function to cleanup the Factories of any Products which - /// were not managed by a ProductPtr or deleted by a ProductDeleter (in - /// other words, the Product was released from its ProductPtr and then its - /// lifecycle was managed by a framework that does not know it has special - /// deletion requirements). - /// - /// If you never call the .release() function on a ProductPtr, then you will - /// never need to call this function. - /// - /// \warning Note that this function should be called ONLY while there are - /// no Products that are actively destructing, or else there is a miniscule - /// probability of causing a segmentation fault. This is never an issue in a - /// single-threaded application. - /// - /// \warning If you have a multi-threaded application where you have - /// absolutely no control over the lifecycle of the products, and you cannot - /// reliably predict a safe window in which you know that no products are - /// being actively deleted, then you can specify a short safety wait to the - /// cleanup call. If any products were being deleted while this function is - /// called, this wait can give them time to fully exit their destructors - /// before we unload their libraries. - /// - /// \note For some applications, it might not be important if there are tiny - /// memory leaks or if plugin libraries remain loaded until the application - /// exits. For those applications, it is okay to not bother calling this - /// function at all. - /// - /// \param[in] _safetyWait - /// For multi-threaded applications, this short waiting window gives time - /// for products that are currently being deleted to exit their - /// destructors before we unload their libraries. If you can reliably - /// predict a window of time in which no products are actively being - /// destructed (or if you have a single-threaded application), then it is - /// okay to set this waiting time to 0. Note that any threads which are - /// about to destruct a product will be blocked until this wait is over. - void IGNITION_PLUGIN_VISIBLE CleanupLostProducts( - const std::chrono::nanoseconds &_safetyWait = - std::chrono::nanoseconds(5)); - - /// \brief Get the number of lost products that have currently accumulated - /// since the last time CleanupLostProducts() was called (or since the - /// program began, if CleanupLostProducts() has not been called yet). - std::size_t IGNITION_PLUGIN_VISIBLE LostProductCount(); - } -} - -#include - -#endif +#include diff --git a/core/include/ignition/plugin/Info.hh b/core/include/ignition/plugin/Info.hh index aed1a315..c5982cf6 100644 --- a/core/include/ignition/plugin/Info.hh +++ b/core/include/ignition/plugin/Info.hh @@ -15,91 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_INFO_HH_ -#define IGNITION_PLUGIN_INFO_HH_ - -#include -#include -#include -#include -#include - -#include - -#include - -namespace ignition -{ - namespace plugin - { - /// \brief sentinel value to check if a plugin was built with the same - /// version of the Info struct - // - /// This must be incremented when the Info struct changes - const int INFO_API_VERSION = 1; - - // We use an inline namespace to assist in forward-compatibility. Eventually - // we may want to support a version-2 of the Info API, in which case - // we will remove the "inline" declaration here, and create a new inline - // namespace called "v2". This original Info object will continue to - // be accessible for backwards compatibility, and even its symbol name in - // the ABI should remain the same. - inline namespace v1 - { - /// \brief Holds info required to construct a plugin - struct IGNITION_PLUGIN_VISIBLE Info - { - /// \brief Clear out all information contained in this Info object - void Clear(); - - /// \brief The name of the plugin - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - std::string name; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - - /// \brief Alternative names that may be used to instantiate the plugin - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - std::set aliases; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - - /// \brief The keys are the names of the types of interfaces that this - /// plugin provides. The values are functions that convert a void - /// pointer (which actually points to the plugin instance) to another - /// void pointer (which actually points to the location of the interface - /// within the plugin instance). - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - using InterfaceCastingMap = - std::unordered_map< std::string, std::function >; - InterfaceCastingMap interfaces; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - - /// \brief This is a set containing the demangled versions of the names - /// of the interfaces provided by this plugin. This gets filled in by - /// the Loader after receiving the Info. It is only used by - /// the user-facing API. Internally, when looking up Interfaces, the - /// mangled `interfaces` map will still be used. - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - std::set demangledInterfaces; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - - /// \brief A method that instantiates a new instance of a plugin - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - std::function factory; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - - /// \brief A method that safely deletes an instance of the plugin - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - std::function deleter; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - }; - } - - /// This typedef is used simultaneously by detail/Register.hh and Loader.cc, - /// so we store it in a location that is visible to both of them. - using InfoMap = std::unordered_map; - using ConstInfoPtr = std::shared_ptr; - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/Plugin.hh b/core/include/ignition/plugin/Plugin.hh index 72e2f886..7c3e8d10 100644 --- a/core/include/ignition/plugin/Plugin.hh +++ b/core/include/ignition/plugin/Plugin.hh @@ -15,179 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_PLUGIN_HH_ -#define IGNITION_PLUGIN_PLUGIN_HH_ - -#include -#include -#include - -#include - -#include -#include - -namespace ignition -{ - namespace plugin - { - // Forward declaration - namespace detail { - template class ComposePlugin; - template class SelectSpecializers; - } - class EnablePluginFromThis; - class WeakPluginPtr; - - class IGNITION_PLUGIN_VISIBLE Plugin - { - // -------------------- Public API --------------------- - - /// \brief Get an interface of the specified type, if it is provided by - /// this plugin. - /// - /// Note that the interface pointer you receive is owned by the Plugin - /// object. You MUST NOT ever try to deallocate it yourself. Moreover, the - /// pointer will be invalidated once all Plugin objects that refer to the - /// same Plugin instance are destructed. Use the QueryInterfaceSharedPtr - /// function in order to get a reference-counting pointer to an interface - /// of this Plugin object. The pointer will remain valid as long as the - /// std::shared_ptr provided by QueryInterfaceSharedPtr is alive. - /// - /// \return A raw pointer to the specified interface. If the requested - /// _interfaceName is not provided by this Plugin, this returns a nullptr. - /// This pointer is invalidated when the reference count of the plugin - /// instance drops to zero. - public: template - Interface *QueryInterface(); - - /// \brief const-qualified version of QueryInterface() - public: template - const Interface *QueryInterface() const; - - /// \brief Get the requested interface as a std::shared_ptr. The template - /// argument Interface must exactly match the underlying type associated - /// with _interfaceName, or else the behavior of this function is - /// undefined. - /// - /// This std::shared_ptr and the interface+plugin that it refers to will - /// remain valid, even if all Plugin objects which refer to the plugin - /// instance are destructed. - /// - /// You MUST NOT attempt to pass a QueryInterface pointer into a - /// std::shared_ptr yourself; that will result in double-delete memory - /// errors. You must always call QueryInterfaceSharedPtr for a reference- - /// counting pointer to an interface. - /// - /// \return A reference-counting pointer to the specified interface. This - /// will keep the interface valid and the plugin instance alive, even if - /// all Plugin objects that refer to this plugin instance are destructed. - public: template - std::shared_ptr QueryInterfaceSharedPtr(); - - /// \brief Same as QueryInterfaceSharedPtr(), but it returns a - /// std::shared_ptr to a const-qualified Interface. - public: template - std::shared_ptr QueryInterfaceSharedPtr() const; - - /// \brief Checks if this Plugin has the specified type of interface. - /// \return Returns true if this Plugin has the specified type of - /// interface, and false otherwise. - public: template - bool HasInterface() const; - - /// \brief Checks if this Plugin has the specified type of interface. - /// - /// By default, we expect you to pass in a demangled version of the - /// interface name. If you want to use a mangled version of the name, - /// set the `demangled` argument to false. - /// - /// \param[in] _interfaceName The name of the desired interface, as a - /// std::string. Note that this expects the name to be mangled. - /// \param[in] _demangled If _interfaceName is demangled, set this to - /// true. If you are instead using the raw mangled name that gets provided - /// by typeid(T).name(), then set _demangled to false. - /// \return Returns true if this Plugin has the specified type of - /// interface, and false otherwise. - public: bool HasInterface(const std::string &_interfaceName, - const bool _demangled = true) const; - - /// \brief Gets the name of this Plugin. - /// \return A pointer to the name of this Plugin, or nullptr if this - /// Plugin is not instantiated. - public: const std::string *Name() const; - - // -------------------- Private API ----------------------- - - template friend class TemplatePluginPtr; - template friend class SpecializedPlugin; - template friend class detail::ComposePlugin; - template friend class detail::SelectSpecializers; - friend class EnablePluginFromThis; - friend class WeakPluginPtr; - - /// \brief Default constructor. This is kept protected to discourage users - /// from instantiating them directly. They should instead only be - /// retrieved as a PluginPtr from the plugin Loader. - protected: Plugin(); - - /// \brief Type-agnostic retriever for interfaces - private: void *PrivateQueryInterface( - const std::string &_interfaceName) const; - - /// \brief Copy the plugin instance from another Plugin object - private: void PrivateCopyPluginInstance(const Plugin &_other) const; - - /// \brief Copy an existing plugin instance into this plugin - /// \param[in] _info - /// Pointer to the Info for this plugin - /// \param[in] _instancePtr - /// Pointer to an already-existing abstract plugin instance pointer - private: void PrivateCopyPluginInstance( - const ConstInfoPtr &_info, - const std::shared_ptr &_instancePtr) const; - - /// \brief Create a new plugin instance based on the info provided - /// \param[in] _info - /// Pointer to the Info for this plugin - /// \param[in] _dlHandlePtr - /// Reference counter for the dl handle of this Plugin - private: void PrivateCreatePluginInstance( - const ConstInfoPtr &_info, - const std::shared_ptr &_dlHandlePtr) const; - - /// \brief Get a reference to the abstract instance being managed by this - /// wrapper - private: const std::shared_ptr &PrivateGetInstancePtr() const; - - /// \brief Get a reference to the Info being used by this wrapper - private: const ConstInfoPtr &PrivateGetInfoPtr() const; - - /// \brief The InterfaceMap type needs to get used in several places, like - /// Plugin::Implementation and SpecializedPlugin. We make the typedef - /// public so that those other classes can use it without needing to be - /// friends of Plugin. End-users should not have any need for this - /// typedef. - public: using InterfaceMap = std::map; - - /// \brief Get or create an iterator to the std::map that holds pointers - /// to the various interfaces provided by this plugin instance. - private: InterfaceMap::iterator PrivateGetOrCreateIterator( - const std::string &_interfaceName); - - class Implementation; - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - /// \brief PIMPL pointer to the implementation of this class. - private: const std::unique_ptr dataPtr; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - - /// \brief Virtual destructor - public: virtual ~Plugin(); - }; - } -} - -#include "ignition/plugin/detail/Plugin.hh" - -#endif +#include diff --git a/core/include/ignition/plugin/PluginPtr.hh b/core/include/ignition/plugin/PluginPtr.hh index b67ea7dc..336fb985 100644 --- a/core/include/ignition/plugin/PluginPtr.hh +++ b/core/include/ignition/plugin/PluginPtr.hh @@ -15,209 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_PLUGINPTR_HH_ -#define IGNITION_PLUGIN_PLUGINPTR_HH_ - -#include -#include -#include - -#include - -namespace ignition -{ - namespace plugin - { - // Forward declarations - namespace detail { template class ComposePlugin; } - - /// \brief This class manages the lifecycle of a plugin instance. It can - /// receive a plugin instance from the ignition::plugin::Loader class - /// or by copy-construction or assignment from another PluginPtr instance. - /// - /// This class behaves similarly to a std::shared_ptr where multiple - /// PluginPtr objects can share a single plugin instance, and the plugin - /// instance will not be deleted until all PluginPtr objects that refer to - /// it are either destroyed, cleared, or begin referring to a different - /// plugin instance. - /// - /// A PluginPtr object can be "cast" to a SpecializedPluginPtr object by - /// simply using the copy/move constructor or assignment operator of a - /// SpecializedPluginPtr object. Note that this "cast" does have a small - /// amount of overhead associated with it, but it may result in huge savings - /// after initialization is finished if you frequently access the interfaces - /// that the SpecializedPluginPtr is specialized for. - template - class TemplatePluginPtr final - { - /// \brief Destructor. Deletes this PluginPtr's reference to the plugin - /// instance. Once all PluginPtrs that refer to a plugin instance are - /// deleted, the plugin will also be deleted. - public: ~TemplatePluginPtr() = default; - - /// \brief Default constructor. Creates a PluginPtr object that does not - /// point to any plugin instance. IsEmpty() will return true until a - /// plugin instance is provided. - public: TemplatePluginPtr(); - - /// \brief Copy constructor. This PluginPtr will now point at the same - /// plugin instance as _other, and they will share ownership. - /// \param[in] _other Pointer to plugin being copied. - public: TemplatePluginPtr(const TemplatePluginPtr &_other); - - /// \brief Move constructor. This PluginPtr will take ownership of the - /// plugin instance held by _other. If this PluginPtr was holding an - /// instance to another plugin, that instance will be deleted if no other - /// PluginPtr is referencing which is being moved. - /// \param[in] _other Plugin being moved. - public: TemplatePluginPtr(TemplatePluginPtr &&_other); - - /// \brief Casting constructor. This PluginPtr will now point at the same - /// plugin instance as _other, and they will share ownership. This - /// essentially allows casting between PluginPtrs that are holding - /// different types of plugin wrappers (for example, you can cast a - /// generic PluginPtr to any SpecializedPluginPtr type, or you can cast - /// between different types of specializations). - /// \param[in] _other Pointer to plugin being casted, which is of a - /// different type. - public: template - TemplatePluginPtr( - const TemplatePluginPtr &_other); - - /// \brief Copy assignment operator. This PluginPtr will now point at the - /// same plugin instance as _other, and they will share ownership. If this - /// PluginPtr was holding an instance to another plugin, that instance - /// will be deleted if no other PluginPtr is referencing it. - /// \param[in] _other Pointer to plugin being copied. - public: TemplatePluginPtr &operator =(const TemplatePluginPtr &_other); - - /// \brief Casting operator. This PluginPtr will now point at the same - /// plugin instance as _other, and they will share ownership. This - /// essentially allows casting between PluginPtrs that are holding - /// different types of plugin wrappers. - /// \param[in] _other Pointer to plugin being casted, which is of a - /// different type. - /// \return A reference to this object. - public: template - TemplatePluginPtr &operator =( - const TemplatePluginPtr &_other); - - /// \brief Move assignment operator. This PluginPtr will take ownership - /// of the plugin instance held by _other. If this PluginPtr was holding - /// an instance to another plugin, that instance will be deleted if no - /// other PluginPtr is referencing it. - /// \param[in] _other Plugin being moved. - /// \return A reference to this object. - public: TemplatePluginPtr &operator=(TemplatePluginPtr &&_other); - - /// \brief nullptr assignment operator. Same as calling Clear() - /// \param[in] _null nullptr object. - /// \return A reference to this object. - public: TemplatePluginPtr &operator=(std::nullptr_t _null); - - /// \brief Access the wrapper for the plugin instance and call one of its - /// member functions. - /// \return The ability to call a member function on the underlying Plugin - /// object. - public: PluginType *operator->() const; - - /// \brief Get a reference to the wrapper for the plugin instance that is - /// being managed by this PluginPtr. - /// \return A reference to the underlying Plugin object. - public: PluginType &operator*() const; - - /// \brief Comparison operator. - /// \param[in] _other Plugin to compare to. - /// \returns True if this Plugin is holding the same plugin - /// instance as _other, otherwise returns false. - public: bool operator ==(const TemplatePluginPtr &_other) const; - - /// \brief Comparison operator. - /// \param[in] _other Plugin to compare to. - /// \returns True if the pointer value of the plugin instance held - /// by this PluginPtr is less than the pointer value of the instance held - /// by _other. - public: bool operator <(const TemplatePluginPtr &_other) const; - - /// \brief Comparison operator. - /// \param[in] _other Plugin to compare to. - /// \returns True if the pointer value of the plugin instance held - /// by this PluginPtr is greater than the pointer value of the instance - /// held by _other. - public: bool operator >(const TemplatePluginPtr &_other) const; - - /// \brief Comparison operator. - /// \param[in] _other Plugin to compare to. - /// \returns True if the pointer instance held by this PluginPtr is - /// different from the pointer instance held by _other. - public: bool operator !=(const TemplatePluginPtr &_other) const; - - /// \brief Comparison operator. - /// \param[in] _other Plugin to compare to. - /// \returns True if the value of the pointer instance held by this - /// PluginPtr is less than or equal to the value of the pointer instance - /// held by _other. - public: bool operator <=(const TemplatePluginPtr &_other) const; - - /// \brief Comparison operator. - /// \param[in] _other Plugin to compare to. - /// \returns True if the value of the pointer instance held by this - /// PluginPtr is greater than or equal to the value of the pointer - /// instance held by _other. - public: bool operator >=(const TemplatePluginPtr &_other) const; - - /// \brief Produces a hash for the plugin instance that this PluginPtr is - /// holding. This function allows PluginPtr instances to be used as values - /// in a std::unordered_set or keys in a - /// std::unordered_map. Using this function directly should - /// not normally be necessary. - /// \return A hash of the underlying pointer object. - public: std::size_t Hash() const; - - /// \brief Check whether this is pointing at a valid plugin. - /// \returns False if this PluginPtr is pointing at a valid plugin - /// instance. If it is instead pointing at a nullptr, this returns true. - public: bool IsEmpty() const; - - /// \brief Implicitly convert this PluginPtr to a boolean. - /// \return The opposite value of IsEmpty(). - public: operator bool() const; - - /// \brief Clears the Plugin instance from this PluginPtr. IsEmpty() will - /// return true after this is used, and none of the interfaces will be - /// available any longer. - public: void Clear(); - - /// \brief Pointer to the plugin wrapper that this PluginPtr is managing. - private: std::unique_ptr dataPtr; - - // Declare friendship - friend class Loader; - template friend class TemplatePluginPtr; - - /// \brief Private constructor. Creates a plugin instance based on the - /// Info provided. This should only be called by Loader to ensure that - /// the Info is well-formed, so we keep it private. - /// \param[in] _info An Info instance that was generated by - /// Loader. Alternatively, this can take a nullptr to create an - /// empty PluginPtr. - /// \param[in] _dlHandlePtr A reference count for the DL handle. - private: explicit TemplatePluginPtr( - const ConstInfoPtr &_info, - const std::shared_ptr &_dlHandlePtr); - }; - - /// \brief Typical usage for TemplatePluginPtr is to just hold a generic - /// Plugin type. - using PluginPtr = TemplatePluginPtr; - - /// \brief This produces a PluginPtr whose Plugin wrapper only grants access - /// to const-qualified interfaces of the plugin instance. - using ConstPluginPtr = TemplatePluginPtr; - } -} - -#include "ignition/plugin/detail/PluginPtr.hh" - -#endif +#include diff --git a/core/include/ignition/plugin/SpecializedPlugin.hh b/core/include/ignition/plugin/SpecializedPlugin.hh index 5dcb3063..48b27d75 100644 --- a/core/include/ignition/plugin/SpecializedPlugin.hh +++ b/core/include/ignition/plugin/SpecializedPlugin.hh @@ -15,151 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_SPECIALIZEDPLUGIN_HH_ -#define IGNITION_PLUGIN_SPECIALIZEDPLUGIN_HH_ - -#include -#include "ignition/plugin/Plugin.hh" - -namespace ignition -{ - namespace plugin - { - // Forward declarations - namespace detail { - template class ComposePlugin; - template class SelectSpecializers; - } - - // Forward declaration of the variadic template SpecializedPlugin class. - template - class SpecializedPlugin; - - /// \brief This class allows Plugin instances to have high-speed access to - /// interfaces that can be anticipated at compile time. The plugin does - /// not have to actually offer the specialized interface in order to get - /// this performance improvement. This template is variadic, so it can - /// support arbitrarily many interfaces. - /// - /// Usage example: - /// - /// \code - /// using MySpecialPluginPtr = SpecializedPluginPtr< - /// MyInterface1, FooInterface, MyInterface2, BarInterface>; - /// - /// MySpecialPluginPtr plugin = loader->Instantiate(pluginName); - /// \endcode - /// - /// Then, calling the function - /// - /// \code - /// plugin->QueryInterface(); - /// \endcode - /// - /// will have extremely high-speed associated with it. It will provide - /// direct access to the the `FooInterface*` of `plugin`. If `plugin` does - /// not actually offer `FooInterface`, then it will return a nullptr, still - /// at extremely high speed. - template - class SpecializedPlugin : public virtual Plugin - { - // -------------------- Public API --------------------- - - // Inherit function overloads - public: using Plugin::QueryInterface; - public: using Plugin::QueryInterfaceSharedPtr; - public: using Plugin::HasInterface; - - // Documentation inherited - public: template - Interface *QueryInterface(); - - // Documentation inherited - public: template - const Interface *QueryInterface() const; - - // Documentation inherited - public: template - std::shared_ptr QueryInterfaceSharedPtr(); - - // Documentation inherited - public: template - std::shared_ptr QueryInterfaceSharedPtr() const; - - // Documentation inherited - public: template - bool HasInterface() const; - - - // -------------------- Private API --------------------- - - // Declare friendship - template friend class SpecializedPlugin; - template friend class detail::ComposePlugin; - template friend class detail::SelectSpecializers; - template friend class TemplatePluginPtr; - - /// \brief type is an empty placeholder class which is used by the private - /// member functions to provide two overloads: a high-performance one for - /// the specialized interface, and a normal-performance one for all other - /// Interface types. - private: template struct type { }; - - /// \brief Delegate the function to the standard Plugin method - /// \param[in] _type Empty object meant to guide the compiler to pick the - /// desired implementation. - /// \return Pointer to the intergace - private: template - Interface *PrivateQueryInterface(type); - - /// \brief Use a high-speed accessor to provide this specialized interface - /// \param[in] _type Empty object meant to guide the compiler to pick the - /// desired implementation. - /// \return Pointer to the specialized intergace - private: SpecInterface *PrivateQueryInterface(type); - - /// \brief Delegate the function to the standard Plugin method - /// \param[in] _type Empty object meant to guide the compiler to pick the - /// desired implementation. - /// \return Pointer to the specialized intergace - private: template - const Interface *PrivateQueryInterface(type) const; - - /// \brief Use a high-speed accessor to provide this specialized interface - /// \param[in] _type Empty object meant to guide the compiler to pick the - /// desired implementation. - /// \return Pointer to the specialized intergace - private: const SpecInterface *PrivateQueryInterface( - type) const; - - /// \brief Delegate the function to the standard PluginPtr method - /// \param[in] _type Empty object meant to guide the compiler to pick the - /// desired implementation. - /// \return True if the interface is present. - private: template - bool PrivateHasInterface(type) const; - - /// \brief Use a high-speed accessor to check this specialized interface - /// \param[in] _type Empty object meant to guide the compiler to pick the - /// desired implementation. - /// \return True if the interface is present. - private: bool PrivateHasInterface(type) const; - - // Dev note (MXG): The privateSpecializedInterfaceIterator object must be - // available to the user during their compile time, so it cannot be hidden - // using PIMPL. The iterator is const because it must always point to the - // same entry throughout its entire lifecycle. - /// \brief Iterator that points to the entry of the specialized interface - private: - const Plugin::InterfaceMap::iterator privateSpecializedInterfaceIterator; - - /// \brief Default constructor - protected: SpecializedPlugin(); - }; - } -} - -#include "ignition/plugin/detail/SpecializedPlugin.hh" - -#endif +#include diff --git a/core/include/ignition/plugin/SpecializedPluginPtr.hh b/core/include/ignition/plugin/SpecializedPluginPtr.hh index 30cc436a..e21ac644 100644 --- a/core/include/ignition/plugin/SpecializedPluginPtr.hh +++ b/core/include/ignition/plugin/SpecializedPluginPtr.hh @@ -15,61 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_SPECIALIZEDPLUGINPTR_HH_ -#define IGNITION_PLUGIN_SPECIALIZEDPLUGINPTR_HH_ - -#include "ignition/plugin/PluginPtr.hh" -#include "ignition/plugin/SpecializedPlugin.hh" - -namespace ignition -{ - namespace plugin - { - /// \brief This alias allows PluginPtr instances to have high-speed access - /// to interfaces that can be anticipated at compile time. The plugin does - /// not have to actually offer the specialized interface in order to get - /// this performance improvement. This template is variadic, so it can - /// support arbitrarily many interfaces. - /// - /// Usage example: - /// - /// Suppose you want to instantiate a plugin that might (or might not) have - /// some combination of four interfaces which are known at compile time: - /// `MyInterface1`, `FooInterface`, `MyInterface2`, and `BarInterface`. You - /// can use SpecializedPluginPtr as shown here: - /// - /// \code - /// using MySpecialPluginPtr = SpecializedPluginPtr< - /// MyInterface1, FooInterface, MyInterface2, BarInterface>; - /// - /// MySpecialPluginPtr plugin = loader->Instantiate(pluginName); - /// \endcode - /// - /// Then, calling the function - /// - /// \code - /// plugin->QueryInterface(); - /// \endcode - /// - /// will have extremely high speed associated with it. It will provide - /// direct access to the the `FooInterface*` of `plugin`. If `plugin` does - /// not actually offer `FooInterface`, then it will return a nullptr, still - /// at extremely high speed. - /// - /// This same rule also applies to `MyInterface1`, `MyInterface2`, and - /// `BarInterface`, because those interfaces were also provided to - /// SpecializedPluginPtr<...> at compile time. - template - using SpecializedPluginPtr = - TemplatePluginPtr< SpecializedPlugin >; - - /// \brief This alias creates a specialized PluginPtr whose interfaces are - /// all const-qualified. - template - using ConstSpecializedPluginPtr = - TemplatePluginPtr< const SpecializedPlugin >; - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/WeakPluginPtr.hh b/core/include/ignition/plugin/WeakPluginPtr.hh index a9e6f8ad..2947bcac 100644 --- a/core/include/ignition/plugin/WeakPluginPtr.hh +++ b/core/include/ignition/plugin/WeakPluginPtr.hh @@ -15,88 +15,4 @@ * */ -#ifndef IGNITION_PLUGIN_WEAKPLUGINPTR_HH_ -#define IGNITION_PLUGIN_WEAKPLUGINPTR_HH_ - -#include - -#include - -namespace ignition -{ - namespace plugin - { - /// \brief WeakPluginPtr is a non-reference-holding smart pointer for a - /// Plugin. WeakPluginPtr is analogous to std::weak_ptr where PluginPtr is - /// analogous to std::shared_ptr. - /// - /// Use Lock() to receive a reference-holding PluginPtr that points to the - /// Plugin that this WeakPluginPtr is meant to reference. - /// - /// If the Plugin is deleted while this WeakPluginPtr is referring to it, - /// then Lock() will return an empty PluginPtr, and IsExpired() will return - /// true. - class IGNITION_PLUGIN_VISIBLE WeakPluginPtr - { - /// \brief Default constructor - public: WeakPluginPtr(); - - /// \brief Copy constructor - /// \param[in] _other - /// Another WeakPluginPtr to copy - public: WeakPluginPtr(const WeakPluginPtr &_other); - - /// \brief Move constructor - /// \param[in] _other - /// Another WeakPluginPtr to move from - public: WeakPluginPtr(WeakPluginPtr &&_other); - - /// \brief Construct from a live PluginPtr - /// \param[in] _ptr - /// A live PluginPtr to refer to. As long as the Plugin instance - /// continues to be held by PluginPtr containers, you can use Lock() to - /// retrieve a reference to it. - // cppcheck-suppress noExplicitConstructor - public: WeakPluginPtr(const PluginPtr &_ptr); - - /// \brief Copy assignment operator - /// \param[in] _other - /// Another WeakPluginPtr to copy - /// \return reference to this - public: WeakPluginPtr &operator=(const WeakPluginPtr &_other); - - /// \brief Move assignment operator - /// \param[in] _other - /// Another WeakPluginPtr to move from - /// \return reference to this - public: WeakPluginPtr &operator=(WeakPluginPtr &&_other); - - /// \brief Assign from a live PluginPtr - /// \param[in] _ptr - /// A live PluginPtr to refer to. As long as the Plugin instance - /// continues to be held by PluginPtr containers, you can use Lock() to - /// retrieve a reference to it. - public: WeakPluginPtr &operator=(const PluginPtr &_ptr); - - /// \brief Retrieve the PluginPtr that this WeakPluginPtr refers to, if it - /// is still available. Otherwise, retrieve an empty PluginPtr. - /// \return The PluginPtr that this WeakPluginPtr refers to. - public: PluginPtr Lock() const; - - /// \brief Check whether the referenced Plugin has already expired. - /// \return true if this PluginPtr is expired, false otherwise. - public: bool IsExpired() const; - - /// \brief Destructor - public: ~WeakPluginPtr(); - - private: class Implementation; - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - /// \brief PIMPL pointer to the implementation of this class - private: std::unique_ptr pimpl; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - }; - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/config.hh b/core/include/ignition/plugin/config.hh new file mode 100644 index 00000000..f4b7faa6 --- /dev/null +++ b/core/include/ignition/plugin/config.hh @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include diff --git a/core/include/ignition/plugin/detail/Export.hh b/core/include/ignition/plugin/detail/Export.hh new file mode 100644 index 00000000..4f226316 --- /dev/null +++ b/core/include/ignition/plugin/detail/Export.hh @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include diff --git a/core/include/ignition/plugin/detail/Factory.hh b/core/include/ignition/plugin/detail/Factory.hh index 78ef4044..d972a2fb 100644 --- a/core/include/ignition/plugin/detail/Factory.hh +++ b/core/include/ignition/plugin/detail/Factory.hh @@ -13,136 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ -#ifndef IGNITION_PLUGIN_DETAIL_FACTORY_HH_ -#define IGNITION_PLUGIN_DETAIL_FACTORY_HH_ - -#include -#include - -#include - -#include - -namespace ignition -{ - namespace plugin - { - namespace detail - { - /// \brief This base class gets mixed in with a Product so that the - /// Product can keep track of the factory that produced it. This allows - /// the factory's library to remain loaded (so that the Product's symbols - /// remain available to the application). Without this, the shared library - /// might get unloaded while the Product is still alive, and then the - /// application will experience a segmentation fault as soon as it tries - /// to do anything with the Product (including deleting it). - class IGNITION_PLUGIN_VISIBLE FactoryCounter - { - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - /// \brief A reference to the factory that created this product - private: std::shared_ptr factoryPluginInstancePtr; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - - /// \brief A special destructor that ensures the shared library remains - /// loaded throughout the destruction process of this product. - public: virtual ~FactoryCounter(); - - // friendship declaration - template friend class ignition::plugin::Factory; - template friend class ignition::plugin::ProductDeleter; - }; - } - - template - class ProductDeleter - { - /// \brief This is a unary function for deleting product pointers. It - /// keeps the factory reference alive while the product is being deleted, - /// and then cleans up the factory reference immediately afterwards. - /// - /// This is the recommended method for deleting product pointers. - public: void operator()(Interface *_ptr) - { - detail::FactoryCounter *counter = - dynamic_cast(_ptr); - - std::shared_ptr factoryPluginInstancePtr; - if (counter) - { - // Hold onto the factory instance pointer while the product completes - // its destruction. - // - // At the same time, clear out the product's reference to its factory - // so that it knows that it's being deleted by a ProductDeleter. - // Otherwise, it will intentionally leak its factory reference to - // avoid causing a segmentation fault in the application. - factoryPluginInstancePtr.swap(counter->factoryPluginInstancePtr); - } - - delete _ptr; - } - }; - - template - auto Factory::Construct(Args&&... _args) - -> ProductPtrType - { - return ProductPtrType(this->ImplConstruct(std::forward(_args)...)); - } - - /// \brief Producing provides the implementation of Factory for a specific - /// derivative of Factory's Interface type. That derivative is called - /// Product, which must be a fully-defined class that implements Interface. - /// - /// The mechanism of this class ensures that as long as the Factory output - /// object is alive, the plugin library that it depends on will remain - /// safely loaded. - template - // cppcheck-suppress syntaxError - template - class Factory::Producing - : public Factory - { - /// \brief This class mixes the product implementation with the factory - /// counter so that the factory remains alive and the library remains - /// loaded throughout the lifecycle of the product. - /// - /// Dev note(MXG): FactoryCounter is inherited first so that its - /// destructor gets called last. In case this product is not deleted by a - /// ProductDeleter, having the FactoryCounter destruct last will minimize - /// the amount of time between handing off its shared_ptr to the - /// lostProductManager and the time that the call stack leaves the - /// destructor of this symbol entirely. This makes it less risky to call - /// CleanupLostProducts() in a multi-threaded application. Combined with - /// giving CleanupLostProducts() a brief waiting period, this should allow - /// even completely reckless multi-threaded applications to be able to - /// cleanup its lost products safely. - class ProductWithFactoryCounter - : public detail::FactoryCounter, - public Product - { - /// \brief Forwarding constructor - public: ProductWithFactoryCounter(Args&&... _args) - : Product(std::forward(_args)...) - { - // Do nothing - } - }; - - // Documentation inherited - private: Interface *ImplConstruct(Args&&... _args) override - { - auto *product = - new ProductWithFactoryCounter(std::forward(_args)...); - - product->factoryPluginInstancePtr = this->PluginInstancePtrFromThis(); - - return product; - } - }; - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/detail/Plugin.hh b/core/include/ignition/plugin/detail/Plugin.hh index d2d47c63..8e009d5e 100644 --- a/core/include/ignition/plugin/detail/Plugin.hh +++ b/core/include/ignition/plugin/detail/Plugin.hh @@ -15,66 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_DETAIL_PLUGIN_HH_ -#define IGNITION_PLUGIN_DETAIL_PLUGIN_HH_ - -#include -#include -#include - -namespace ignition -{ - namespace plugin - { - ////////////////////////////////////////////////// - template - Interface *Plugin::QueryInterface() - { - return static_cast( - this->PrivateQueryInterface(typeid(Interface).name())); - } - - ////////////////////////////////////////////////// - template - const Interface *Plugin::QueryInterface() const - { - return static_cast( - this->PrivateQueryInterface(typeid(Interface).name())); - } - - ////////////////////////////////////////////////// - template - std::shared_ptr Plugin::QueryInterfaceSharedPtr() - { - Interface *ptr = this->QueryInterface(); - if (ptr) - return std::shared_ptr(this->PrivateGetInstancePtr(), ptr); - - return nullptr; - } - - ////////////////////////////////////////////////// - template - std::shared_ptr Plugin::QueryInterfaceSharedPtr() const - { - const Interface *ptr = this->QueryInterface(); - if (ptr) - { - return std::shared_ptr( - this->PrivateGetInstancePtr(), ptr); - } - - return nullptr; - } - - ////////////////////////////////////////////////// - template - bool Plugin::HasInterface() const - { - return this->HasInterface(typeid(Interface).name(), false); - } - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/detail/PluginPtr.hh b/core/include/ignition/plugin/detail/PluginPtr.hh index 242c92c9..f812a59f 100644 --- a/core/include/ignition/plugin/detail/PluginPtr.hh +++ b/core/include/ignition/plugin/detail/PluginPtr.hh @@ -15,186 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_DETAIL_PLUGINPTR_HH_ -#define IGNITION_PLUGIN_DETAIL_PLUGINPTR_HH_ - -#include -#include -#include -#include - -namespace ignition -{ - namespace plugin - { - ////////////////////////////////////////////////// - template - TemplatePluginPtr::TemplatePluginPtr() - : dataPtr(new PluginType) - { - // Do nothing - } - - ////////////////////////////////////////////////// - template - TemplatePluginPtr::TemplatePluginPtr( - const TemplatePluginPtr &_other) - : dataPtr(new PluginType) - { - this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); - } - - ////////////////////////////////////////////////// - template - // the following is a false positive with cppcheck 1.82 fixed in 1.83 - // cppcheck-suppress syntaxError - template - TemplatePluginPtr::TemplatePluginPtr( - const TemplatePluginPtr &_other) - : dataPtr(new PluginType) - { - static_assert(ConstCompatible::value, - "The requested PluginPtr cast would discard const qualifiers"); - this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); - } - - ////////////////////////////////////////////////// - template - TemplatePluginPtr& TemplatePluginPtr::operator =( - const TemplatePluginPtr &_other) - { - this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); - return *this; - } - - ////////////////////////////////////////////////// - template - template - TemplatePluginPtr& TemplatePluginPtr::operator =( - const TemplatePluginPtr &_other) - { - static_assert(ConstCompatible::value, - "The requested PluginPtr cast would discard const qualifiers"); - this->dataPtr->PrivateCopyPluginInstance(*_other.dataPtr); - return *this; - } - - ////////////////////////////////////////////////// - template - TemplatePluginPtr::TemplatePluginPtr( - TemplatePluginPtr &&_other) - : dataPtr(std::move(_other.dataPtr)) - { - // Do nothing - } - - ////////////////////////////////////////////////// - template - TemplatePluginPtr& TemplatePluginPtr::operator =( - TemplatePluginPtr &&_other) - { - this->dataPtr = std::move(_other.dataPtr); - return *this; - } - - ////////////////////////////////////////////////// - template - TemplatePluginPtr& TemplatePluginPtr::operator =( - std::nullptr_t) - { - this->Clear(); - return *this; - } - - ////////////////////////////////////////////////// - template - PluginType* TemplatePluginPtr::operator ->() const - { - return dataPtr.get(); - } - - ////////////////////////////////////////////////// - template - PluginType& TemplatePluginPtr::operator *() const - { - return (*dataPtr); - } - - ////////////////////////////////////////////////// - #define DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR(op)\ - template \ - bool TemplatePluginPtr::operator op (\ - const TemplatePluginPtr &_other) const\ - {\ - return (this->dataPtr->PrivateGetInstancePtr() op \ - _other.dataPtr->PrivateGetInstancePtr() );\ - } - - DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( == ) // NOLINT - DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( < ) // NOLINT - DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( > ) // NOLINT - DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( != ) // NOLINT - DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( <= ) // NOLINT - DETAIL_IGN_PLUGIN_PLUGINPTR_IMPLEMENT_OPERATOR( >= ) // NOLINT - - ////////////////////////////////////////////////// - template - std::size_t TemplatePluginPtr::Hash() const - { - return std::hash< std::shared_ptr >()( - this->dataPtr->PrivateGetInstancePtr()); - } - - ////////////////////////////////////////////////// - template - bool TemplatePluginPtr::IsEmpty() const - { - return (nullptr == this->dataPtr->PrivateGetInstancePtr()); - } - - ////////////////////////////////////////////////// - template - TemplatePluginPtr::operator bool() const - { - return !this->IsEmpty(); - } - - ////////////////////////////////////////////////// - template - void TemplatePluginPtr::Clear() - { - this->dataPtr->PrivateCreatePluginInstance(nullptr, nullptr); - } - - ////////////////////////////////////////////////// - template - TemplatePluginPtr::TemplatePluginPtr( - const ConstInfoPtr &_info, - const std::shared_ptr &_dlHandlePtr) - : dataPtr(new PluginType) - { - dataPtr->PrivateCreatePluginInstance(_info, _dlHandlePtr); - } - } -} - -// Note that opening up namespace std is legal here because we are specializing -// a templated structure from the STL, which is permitted (and even encouraged). -namespace std -{ - /// \brief Template specialization that provides a hash function for PluginPtr - /// so that it can easily be used in STL objects like std::unordered_set and - /// std::unordered_map - template - struct hash> - { - size_t operator()( - const ignition::plugin::TemplatePluginPtr &ptr) const - { - return ptr.Hash(); - } - }; -} - -#endif +#include diff --git a/core/include/ignition/plugin/detail/SpecializedPlugin.hh b/core/include/ignition/plugin/detail/SpecializedPlugin.hh index 41853dae..a9275543 100644 --- a/core/include/ignition/plugin/detail/SpecializedPlugin.hh +++ b/core/include/ignition/plugin/detail/SpecializedPlugin.hh @@ -15,305 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_DETAIL_SPECIALIZEDPLUGIN_HH_ -#define IGNITION_PLUGIN_DETAIL_SPECIALIZEDPLUGIN_HH_ - -#include -#include "ignition/plugin/SpecializedPlugin.hh" - -// This preprocessor token should only be used by the unittest that is -// responsible for checking that the specialized routines are being used to -// access specialized plugin interfaces. -#ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS -bool usedSpecializedInterfaceAccess; -#endif - - -namespace ignition -{ - namespace plugin - { - ///////////////////////////////////////////////// - template - // the following is a false positive with cppcheck 1.82 fixed in 1.83 - // cppcheck-suppress syntaxError - template - Interface *SpecializedPlugin::QueryInterface() - { - return this->PrivateQueryInterface(type()); - } - - ///////////////////////////////////////////////// - template - template - const Interface *SpecializedPlugin::QueryInterface() const - { - return this->PrivateQueryInterface(type()); - } - - ///////////////////////////////////////////////// - template - template - std::shared_ptr - SpecializedPlugin::QueryInterfaceSharedPtr() - { - Interface *ptr = this->QueryInterface(); - if (ptr) - return std::shared_ptr(this->PrivateGetInstancePtr(), ptr); - - return nullptr; - } - - ///////////////////////////////////////////////// - template - template - std::shared_ptr - SpecializedPlugin::QueryInterfaceSharedPtr() const - { - const Interface *ptr = this->QueryInterface(); - if (ptr) - return std::shared_ptr(this->PrivateGetInstancePtr(), ptr); - - return nullptr; - } - - ///////////////////////////////////////////////// - template - template - bool SpecializedPlugin::HasInterface() const - { - return this->PrivateHasInterface(type()); - } - - ///////////////////////////////////////////////// - // This function is necessary for compilation, but COMPOSEPLUGIN_DISPATCH - // always seems to call the base Plugin class instead of this. - // So we will exclude it from code coverage. - // LCOV_EXCL_START - template - template - Interface *SpecializedPlugin::PrivateQueryInterface( - type) - { - return this->Plugin::QueryInterface(); - } - // LCOV_EXCL_STOP - - ///////////////////////////////////////////////// - template - SpecInterface *SpecializedPlugin::PrivateQueryInterface( - type) - { - #ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS - usedSpecializedInterfaceAccess = true; - #endif - return static_cast( - this->privateSpecializedInterfaceIterator->second); - } - - ///////////////////////////////////////////////// - template - template - const Interface *SpecializedPlugin:: - PrivateQueryInterface(type) const - { - return this->Plugin::QueryInterface(); - } - - ///////////////////////////////////////////////// - template - const SpecInterface *SpecializedPlugin:: - PrivateQueryInterface(type) const - { - #ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS - usedSpecializedInterfaceAccess = true; - #endif - return static_cast( - this->privateSpecializedInterfaceIterator->second); - } - - ///////////////////////////////////////////////// - template - template - bool SpecializedPlugin::PrivateHasInterface( - type) const - { - return this->Plugin::HasInterface(); - } - - ///////////////////////////////////////////////// - template - bool SpecializedPlugin::PrivateHasInterface( - type) const - { - #ifdef IGNITION_UNITTEST_SPECIALIZED_PLUGIN_ACCESS - usedSpecializedInterfaceAccess = true; - #endif - return (nullptr != this->privateSpecializedInterfaceIterator->second); - } - - ///////////////////////////////////////////////// - template - SpecializedPlugin::SpecializedPlugin() - : privateSpecializedInterfaceIterator( - this->PrivateGetOrCreateIterator( - typeid(SpecInterface).name())) - { - // Do nothing - } - - namespace detail - { - /// \brief This template provides an implementation of - /// SelectSpecializerIfAvailable by having two template specializations - /// to choose between at compile time. - /// - /// If specialized is true, then this will provide the specializer for - /// Interface as `Specializer`. - template - struct SelectSpecalizerIfAvailableImpl - { - using Specializer = SpecializedPlugin; - }; - - /// \brief This template specialization will be invoked when - /// Specialization is not specialized for Interface, and therefore return - /// the generic Plugin type. - template - struct SelectSpecalizerIfAvailableImpl - { - using Specializer = Plugin; - }; - - /// \brief If Specialization contains a leaf specializer for Interface, - /// i.e. SpecializedPlugin, then this will provide that type - /// under the name `Specializer`. Otherwise, this will simply provide the - /// generic Plugin type. - template - struct SelectSpecalizerIfAvailable - { - using Specializer = typename SelectSpecalizerIfAvailableImpl, Specialization>::value - >::Specializer; - }; - - template - class SelectSpecializers : public virtual Base - { - /// \brief Default destructor - public: virtual ~SelectSpecializers() = default; - - // Inherit function overloads - using Plugin::QueryInterface; - using Plugin::QueryInterfaceSharedPtr; - using Plugin::HasInterface; - - // Used for template metaprogramming - using Specialization = Base; - - /// \brief Implement functions whose only roles are to dispatch their - /// functionalities between two base classes, depending on which base is - /// specialized for the template type. This must only be called within - /// the ComposePlugin class. - /// - /// The dispatch is performed by casting this object to the type that - /// specializes for the requested Interface, if such a type is availabe - /// within its inheritance structure. Otherwise, we cast to the generic - /// Plugin type. - #define DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( \ - ReturnType, Function, Suffix, CastTo, Args) \ - public: \ - template \ - ReturnType Function Suffix \ - { \ - using Specializer = typename detail::SelectSpecalizerIfAvailable< \ - T, Specialization>::Specializer; \ - return static_cast(this)->template Function Args; \ - } - - - DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( - T*, QueryInterface, (), Specializer, ()) - - DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( - const T*, QueryInterface, () const, const Specializer, ()) - - DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( - std::shared_ptr, QueryInterfaceSharedPtr, (), Specializer, ()) - - DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( - std::shared_ptr, QueryInterfaceSharedPtr, - () const, const Specializer, ()) - - DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH( - bool, HasInterface, () const, const Specializer, ()) - - // Declare friendship - template friend class ignition::plugin::SpecializedPlugin; - template friend class SelectSpecializers; - template friend class ComposePlugin; - - protected: SelectSpecializers() = default; - }; - - /// \brief ComposePlugin provides a way for a multi-specialized Plugin - /// type to find its specializations within itself each time an - /// interface-querying function is called. The macro - /// DETAIL_IGN_PLUGIN_COMPOSEPLUGIN_DISPATCH accomplishes this for each - /// of the different functions by doing a compile-time check on whether - /// Base2 contains the specialization, and then picks Base1 if it does - /// not. - template - class ComposePlugin : public virtual Base1, public virtual Base2 - { - // Used for template metaprogramming - using Specialization = ComposePlugin; - - // Declare friendship - template friend class ignition::plugin::SpecializedPlugin; - template friend class SelectSpecializers; - template friend class ComposePlugin; - - protected: ComposePlugin() = default; - }; - - /// \brief This template specialization is used when Base1 and Base2 are - /// identical. It dodges a compilation error that occurs when a class - /// tries to double-inherit a direct base class. - template - class ComposePlugin - : public virtual RepeatedBase { }; // NOLINT - } - - /// \brief Construct an unbalanced binary tree of specializations by - /// convoluting SpecializedPlugin types using ComposePlugin. - template - class SpecializedPlugin : - public virtual detail::SelectSpecializers< - detail::ComposePlugin< - SpecializedPlugin, - SpecializedPlugin - > - > - { - // Declare friendship - template friend class SpecializedPlugin; - template friend class detail::ComposePlugin; - template friend class detail::SelectSpecializers; - template friend class TemplatePluginPtr; - - /// \brief Virtual destructor - public: virtual ~SpecializedPlugin() = default; - - /// \brief Default constructor - protected: SpecializedPlugin() = default; - }; - - /// \brief Allow empty specializations of SpecializedPlugin - template <> - class SpecializedPlugin<> : public virtual Plugin { }; // NOLINT - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/detail/utility.hh b/core/include/ignition/plugin/detail/utility.hh index 1f6d32f1..38c344de 100644 --- a/core/include/ignition/plugin/detail/utility.hh +++ b/core/include/ignition/plugin/detail/utility.hh @@ -15,33 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_DETAIL_UTILITY_HH_ -#define IGNITION_PLUGIN_DETAIL_UTILITY_HH_ - - -#include - -namespace ignition -{ - namespace plugin - { - namespace detail - { - ////////////////////////////////////////////////// - template - struct ConstCompatible : std::true_type - { - }; - - ////////////////////////////////////////////////// - template - struct ConstCompatible - : std::integral_constant::value> - { - }; - } - } -} - -#endif +#include diff --git a/core/include/ignition/plugin/utility.hh b/core/include/ignition/plugin/utility.hh index 12e60220..a09a8639 100644 --- a/core/include/ignition/plugin/utility.hh +++ b/core/include/ignition/plugin/utility.hh @@ -15,50 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_UTILITY_HH_ -#define IGNITION_PLUGIN_UTILITY_HH_ - -#include - -#include -#include - -namespace ignition -{ - namespace plugin - { - ///////////////////////////////////////////////// - /// \brief Contains a static constexpr field named `value` which will be - /// true if the type `From` has a const-quality less than or equal to the - /// type `To`. - /// - /// The following expressions will return true: - /// - /// \code - /// ConstCompatible::value - /// ConstCompatible::value - /// \endcode - /// - /// The following expression will return false: - /// - /// \code - /// ConstCompatible::value - /// \endcode - /// - template - using ConstCompatible = detail::ConstCompatible; - - - ///////////////////////////////////////////////// - /// \brief Demangle the ABI typeinfo name of a symbol into a human-readable - /// version. - /// \param[in] _symbol - /// Pass in the result of typeid(T).name() - /// \return The demangled (human-readable) version of the symbol name - std::string IGNITION_PLUGIN_VISIBLE DemangleSymbol( - const std::string &_symbol); - } -} - -#endif +#include diff --git a/examples/integrators.cc b/examples/integrators.cc index 77c1e561..5be94e65 100644 --- a/examples/integrators.cc +++ b/examples/integrators.cc @@ -22,8 +22,8 @@ #include #include -#include -#include +#include +#include #include "plugins/integrators.hh" diff --git a/examples/plugins/ExponentialODE.cc b/examples/plugins/ExponentialODE.cc index e853f02f..a6b8e7f0 100644 --- a/examples/plugins/ExponentialODE.cc +++ b/examples/plugins/ExponentialODE.cc @@ -17,7 +17,7 @@ #include -#include +#include #include "integrators.hh" diff --git a/examples/plugins/ForwardEuler.cc b/examples/plugins/ForwardEuler.cc index d62013e5..636d97b6 100644 --- a/examples/plugins/ForwardEuler.cc +++ b/examples/plugins/ForwardEuler.cc @@ -17,7 +17,7 @@ #include -#include +#include #include "integrators.hh" diff --git a/examples/plugins/PolynomialODE.cc b/examples/plugins/PolynomialODE.cc index bce1b05d..d91a45de 100644 --- a/examples/plugins/PolynomialODE.cc +++ b/examples/plugins/PolynomialODE.cc @@ -17,7 +17,7 @@ #include -#include +#include #include "integrators.hh" diff --git a/examples/plugins/RungeKutta4.cc b/examples/plugins/RungeKutta4.cc index 7cd042f4..dbfa9e3f 100644 --- a/examples/plugins/RungeKutta4.cc +++ b/examples/plugins/RungeKutta4.cc @@ -17,7 +17,7 @@ #include -#include +#include #include "integrators.hh" diff --git a/examples/plugins/integrators.hh b/examples/plugins/integrators.hh index 7a1238d5..b83c2a59 100644 --- a/examples/plugins/integrators.hh +++ b/examples/plugins/integrators.hh @@ -15,8 +15,8 @@ * */ -#ifndef IGNITION_PLUGIN_EXAMPLES_PLUGINS_INTEGRATORS_HH_ -#define IGNITION_PLUGIN_EXAMPLES_PLUGINS_INTEGRATORS_HH_ +#ifndef GZ_PLUGIN_EXAMPLES_PLUGINS_INTEGRATORS_HH_ +#define GZ_PLUGIN_EXAMPLES_PLUGINS_INTEGRATORS_HH_ #include #include diff --git a/examples/plugins/robot.hh b/examples/plugins/robot.hh index 585711ca..f50ce215 100644 --- a/examples/plugins/robot.hh +++ b/examples/plugins/robot.hh @@ -15,15 +15,15 @@ * */ -#ifndef IGNITION_PLUGIN_EXAMPLES_PLUGINS_ROBOT_HH_ -#define IGNITION_PLUGIN_EXAMPLES_PLUGINS_ROBOT_HH_ +#ifndef GZ_PLUGIN_EXAMPLES_PLUGINS_ROBOT_HH_ +#define GZ_PLUGIN_EXAMPLES_PLUGINS_ROBOT_HH_ #include -#include -#include +#include +#include -#include +#include namespace ignition { diff --git a/examples/robot.cc b/examples/robot.cc index b258231c..7cd30dba 100644 --- a/examples/robot.cc +++ b/examples/robot.cc @@ -22,9 +22,9 @@ #include "plugins/robot.hh" -#include -#include -#include +#include +#include +#include #ifdef HAVE_BOOST_PROGRAM_OPTIONS #include diff --git a/loader/include/gz/plugin/Loader.hh b/loader/include/gz/plugin/Loader.hh new file mode 100644 index 00000000..e10fc8e0 --- /dev/null +++ b/loader/include/gz/plugin/Loader.hh @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_LOADER_HH_ +#define GZ_PLUGIN_LOADER_HH_ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace ignition +{ + namespace plugin + { + /// \brief Class for loading plugins + class IGNITION_PLUGIN_LOADER_VISIBLE Loader + { + /// \brief Constructor + public: Loader(); + + /// \brief Destructor + public: ~Loader(); + + /// \brief Makes a printable string with info about plugins + /// + /// \returns A pretty string + public: std::string PrettyStr() const; + + /// \brief Get demangled names of interfaces that the loader has plugins + /// for. + /// + /// \returns Demangled names of the interfaces that are implemented + public: std::unordered_set InterfacesImplemented() const; + + /// \brief Get plugin names that implement the specified interface + /// + /// \return names of plugins that implement the interface. + public: template + std::unordered_set PluginsImplementing() const; + + /// \brief Get plugin names that implement the specified interface string. + /// Note that the templated version of this function is recommended + /// instead of this version to avoid confusion about whether a mangled or + /// demangled version of a string is being used. Note that the function + /// InterfacesImplemented() returns demangled versions of the interface + /// names. + /// + /// If you want to pass in a mangled version of an interface name, e.g. + /// the result that would be produced by typeid(T).name(), then set + /// `demangled` to false. + /// + /// \param[in] _interface + /// Name of an interface + /// + /// \param[in] _demangled + /// Specify whether the _interface string is demangled (default, true) + /// or mangled (false). + /// + /// \returns Names of plugins that implement the interface + public: std::unordered_set PluginsImplementing( + const std::string &_interface, + const bool _demangled = true) const; + + /// \brief Get a set of the names of all plugins that are currently known + /// to this Loader. + /// \return A set of all plugin names known to this Loader. + public: std::set AllPlugins() const; + + /// \brief Get plugin names that correspond to the specified alias string. + /// + /// If there is more than one entry in this set, then the alias cannot be + /// used to instantiate any of those plugins. + /// + /// If the name of a plugin matches the alias string, then that plugin + /// will be instantiated any time the string is used to instantiate a + /// plugin, no matter how many other plugins use the alias. + /// + /// \param[in] _alias + /// The std::string of the alias + /// + /// \return A set of plugins that correspond to the desired alias + public: std::set PluginsWithAlias( + const std::string &_alias) const; + + /// \brief Get the aliases of the plugin with the given name + /// + /// \param[in] _pluginName + /// The name of the desired plugin + /// + /// \return A set of aliases corresponding to the desired plugin + public: std::set AliasesOfPlugin( + const std::string &_pluginName) const; + + /// \brief Resolve the plugin name or alias into the name of the plugin + /// that it maps to. If this is a name or alias that does not uniquely map + /// to a known plugin, then the return value will be an empty string. + /// + /// \param[in] _nameOrAlias + /// The name or alias of the plugin of interest. + /// + /// \return The name of the plugin being referred to, or an empty string + /// if no such plugin is known. + public: std::string LookupPlugin(const std::string &_nameOrAlias) const; + + /// \brief Load a library at the given path + /// + /// \param[in] _pathToLibrary + /// The path to a library + /// + /// \returns The set of plugins that have been loaded from the library + public: std::unordered_set LoadLib( + const std::string &_pathToLibrary); + + /// \brief Instantiates a plugin for the given plugin name + /// + /// \param[in] _pluginNameOrAlias + /// Name or alias of the plugin to instantiate. + /// + /// \returns Pointer to instantiated plugin + public: PluginPtr Instantiate( + const std::string &_pluginNameOrAlias) const; + + /// \brief Instantiates a plugin of PluginType for the given plugin name. + /// This can be used to create a specialized PluginPtr. + /// + /// \tparam PluginPtrType + /// The specialized type of PluginPtrPtr that you + /// want to construct. + /// + /// \param[in] _pluginNameOrAlias + /// Name or alias of the plugin that you want to instantiate. + /// + /// \returns pointer for the instantiated PluginPtr + public: template + PluginPtrType Instantiate(const std::string &_pluginNameOrAlias) const; + + /// \brief Instantiates a plugin for the given plugin name, and then + /// returns a reference-counting interface corresponding to InterfaceType. + /// + /// If you use this function to retrieve a Factory, you can call + /// Construct(...) on the returned interface, as long as the returned + /// interface is not a nullptr. + /// + /// \remark This function is identical to: + /// + /// \code + /// loader->Instantiate(_pluginNameOrAlias) + /// ->QueryInterfaceSharedPtr(); + /// \endcode + /// + /// \tparam InterfaceType + /// The type of interface to look for. This function is meant for + /// producing Factories, but any type of Interface can be requested. + /// + /// \param[in] _pluginNameOrAlias + /// Name or alias of the plugin that you want to use for production. + /// + /// \return reference to an InterfaceType if it can be provided by the + /// requested plugin. + public: template + std::shared_ptr Factory( + const std::string &_pluginNameOrAlias) const; + + /// \brief This loader will forget about the library at the given path + /// location. If you want to instantiate a plugin from this library using + /// this loader, you will first need to call LoadLib again. + /// + /// After this function has been called, once all plugin instances that + /// are tied to the library have been deleted, the library will + /// automatically be unloaded from the executable. Note that when this + /// Loader leaves scope (or gets deleted), it will have the same + /// effect as calling ForgetLibrary on all of the libraries that it + /// loaded, so there is generally no need to call this function. However, + /// it may be useful if you want to reduce clutter in the Loader + /// instance or let go of library resources that are no longer being used. + /// + /// Note that even if you have released all references to a library, it is + /// still up to the discretion of your operating system whether (or when) + /// that library will be unloaded. In some cases, the operating system + /// might not choose to unload it until the program exits completely. + /// + /// \param[in] _pathToLibrary + /// Path to the library that you want to forget + /// + /// \return True if the library was actively loaded and is now + /// successfully forgotten. If the library was not actively loaded, this + /// returns false. + public: bool ForgetLibrary(const std::string &_pathToLibrary); + + /// \brief Forget the library that provides the plugin with the given + /// name. Note that this will also forget all other plugin types which + /// are provided by that library. + /// + /// \param[in] _pluginNameOrAlias + /// Name or alias of the plugin whose library you want to forget. + /// + /// \sa bool ForgetLibrary(const std::string &_pathToLibrary) + public: bool ForgetLibraryOfPlugin(const std::string &_pluginNameOrAlias); + + /// \brief Get a pointer to the Info corresponding to _pluginName. + /// + /// \param[in] _resolvedName + /// The resolved name, i.e. the demangled class symbol name as returned + /// by LookupPlugin(~), of the plugin that you want to instantiate. + /// + /// \return Pointer to the corresponding Info, or nullptr if there + /// is no info for the requested _pluginName. + private: ConstInfoPtr PrivateGetInfo( + const std::string &_resolvedName) const; + + /// \brief Get a std::shared_ptr that manages the lifecycle of the shared + /// library handle which provides the specified plugin. + /// + /// \param[in] _resolvedName + /// The resolved name, i.e. the demangled class symbol name as returned + /// by LookupPlugin(~), of the plugin that you want to instantiate. + /// + /// \return Reference-counting pointer to a library handle, or else a + /// nullptr if the plugin is not available. + private: std::shared_ptr PrivateGetPluginDlHandlePtr( + const std::string &_resolvedName) const; + + class Implementation; + IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING + /// \brief PIMPL pointer to class implementation + private: std::unique_ptr dataPtr; + IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING + }; + } +} + +#include + +#endif diff --git a/loader/include/gz/plugin/detail/Loader.hh b/loader/include/gz/plugin/detail/Loader.hh new file mode 100644 index 00000000..ce5ce0f6 --- /dev/null +++ b/loader/include/gz/plugin/detail/Loader.hh @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef GZ_PLUGIN_DETAIL_LOADER_HH_ +#define GZ_PLUGIN_DETAIL_LOADER_HH_ + +#include +#include +#include +#include +#include + +namespace ignition +{ + namespace plugin + { + template + std::unordered_set Loader::PluginsImplementing() const + { + return this->PluginsImplementing(typeid(Interface).name(), false); + } + + template + PluginPtrType Loader::Instantiate( + const std::string &_pluginNameOrAlias) const + { + const std::string &resolvedName = this->LookupPlugin(_pluginNameOrAlias); + if (resolvedName.empty()) + return PluginPtr(); + + PluginPtrType ptr(this->PrivateGetInfo(resolvedName), + this->PrivateGetPluginDlHandlePtr(resolvedName)); + + if (auto *enableFromThis = + ptr->template QueryInterface()) + enableFromThis->PrivateSetPluginFromThis(ptr); + + return ptr; + } + + template + std::shared_ptr Loader::Factory( + const std::string &_pluginNameOrAlias) const + { + return this->Instantiate(_pluginNameOrAlias) + ->template QueryInterfaceSharedPtr(); + } + } +} + +#endif diff --git a/loader/include/ignition/plugin/Loader.hh b/loader/include/ignition/plugin/Loader.hh index e5433eee..eeabec39 100644 --- a/loader/include/ignition/plugin/Loader.hh +++ b/loader/include/ignition/plugin/Loader.hh @@ -15,242 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_LOADER_HH_ -#define IGNITION_PLUGIN_LOADER_HH_ - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace ignition -{ - namespace plugin - { - /// \brief Class for loading plugins - class IGNITION_PLUGIN_LOADER_VISIBLE Loader - { - /// \brief Constructor - public: Loader(); - - /// \brief Destructor - public: ~Loader(); - - /// \brief Makes a printable string with info about plugins - /// - /// \returns A pretty string - public: std::string PrettyStr() const; - - /// \brief Get demangled names of interfaces that the loader has plugins - /// for. - /// - /// \returns Demangled names of the interfaces that are implemented - public: std::unordered_set InterfacesImplemented() const; - - /// \brief Get plugin names that implement the specified interface - /// - /// \return names of plugins that implement the interface. - public: template - std::unordered_set PluginsImplementing() const; - - /// \brief Get plugin names that implement the specified interface string. - /// Note that the templated version of this function is recommended - /// instead of this version to avoid confusion about whether a mangled or - /// demangled version of a string is being used. Note that the function - /// InterfacesImplemented() returns demangled versions of the interface - /// names. - /// - /// If you want to pass in a mangled version of an interface name, e.g. - /// the result that would be produced by typeid(T).name(), then set - /// `demangled` to false. - /// - /// \param[in] _interface - /// Name of an interface - /// - /// \param[in] _demangled - /// Specify whether the _interface string is demangled (default, true) - /// or mangled (false). - /// - /// \returns Names of plugins that implement the interface - public: std::unordered_set PluginsImplementing( - const std::string &_interface, - const bool _demangled = true) const; - - /// \brief Get a set of the names of all plugins that are currently known - /// to this Loader. - /// \return A set of all plugin names known to this Loader. - public: std::set AllPlugins() const; - - /// \brief Get plugin names that correspond to the specified alias string. - /// - /// If there is more than one entry in this set, then the alias cannot be - /// used to instantiate any of those plugins. - /// - /// If the name of a plugin matches the alias string, then that plugin - /// will be instantiated any time the string is used to instantiate a - /// plugin, no matter how many other plugins use the alias. - /// - /// \param[in] _alias - /// The std::string of the alias - /// - /// \return A set of plugins that correspond to the desired alias - public: std::set PluginsWithAlias( - const std::string &_alias) const; - - /// \brief Get the aliases of the plugin with the given name - /// - /// \param[in] _pluginName - /// The name of the desired plugin - /// - /// \return A set of aliases corresponding to the desired plugin - public: std::set AliasesOfPlugin( - const std::string &_pluginName) const; - - /// \brief Resolve the plugin name or alias into the name of the plugin - /// that it maps to. If this is a name or alias that does not uniquely map - /// to a known plugin, then the return value will be an empty string. - /// - /// \param[in] _nameOrAlias - /// The name or alias of the plugin of interest. - /// - /// \return The name of the plugin being referred to, or an empty string - /// if no such plugin is known. - public: std::string LookupPlugin(const std::string &_nameOrAlias) const; - - /// \brief Load a library at the given path - /// - /// \param[in] _pathToLibrary - /// The path to a library - /// - /// \returns The set of plugins that have been loaded from the library - public: std::unordered_set LoadLib( - const std::string &_pathToLibrary); - - /// \brief Instantiates a plugin for the given plugin name - /// - /// \param[in] _pluginNameOrAlias - /// Name or alias of the plugin to instantiate. - /// - /// \returns Pointer to instantiated plugin - public: PluginPtr Instantiate( - const std::string &_pluginNameOrAlias) const; - - /// \brief Instantiates a plugin of PluginType for the given plugin name. - /// This can be used to create a specialized PluginPtr. - /// - /// \tparam PluginPtrType - /// The specialized type of PluginPtrPtr that you - /// want to construct. - /// - /// \param[in] _pluginNameOrAlias - /// Name or alias of the plugin that you want to instantiate. - /// - /// \returns pointer for the instantiated PluginPtr - public: template - PluginPtrType Instantiate(const std::string &_pluginNameOrAlias) const; - - /// \brief Instantiates a plugin for the given plugin name, and then - /// returns a reference-counting interface corresponding to InterfaceType. - /// - /// If you use this function to retrieve a Factory, you can call - /// Construct(...) on the returned interface, as long as the returned - /// interface is not a nullptr. - /// - /// \remark This function is identical to: - /// - /// \code - /// loader->Instantiate(_pluginNameOrAlias) - /// ->QueryInterfaceSharedPtr(); - /// \endcode - /// - /// \tparam InterfaceType - /// The type of interface to look for. This function is meant for - /// producing Factories, but any type of Interface can be requested. - /// - /// \param[in] _pluginNameOrAlias - /// Name or alias of the plugin that you want to use for production. - /// - /// \return reference to an InterfaceType if it can be provided by the - /// requested plugin. - public: template - std::shared_ptr Factory( - const std::string &_pluginNameOrAlias) const; - - /// \brief This loader will forget about the library at the given path - /// location. If you want to instantiate a plugin from this library using - /// this loader, you will first need to call LoadLib again. - /// - /// After this function has been called, once all plugin instances that - /// are tied to the library have been deleted, the library will - /// automatically be unloaded from the executable. Note that when this - /// Loader leaves scope (or gets deleted), it will have the same - /// effect as calling ForgetLibrary on all of the libraries that it - /// loaded, so there is generally no need to call this function. However, - /// it may be useful if you want to reduce clutter in the Loader - /// instance or let go of library resources that are no longer being used. - /// - /// Note that even if you have released all references to a library, it is - /// still up to the discretion of your operating system whether (or when) - /// that library will be unloaded. In some cases, the operating system - /// might not choose to unload it until the program exits completely. - /// - /// \param[in] _pathToLibrary - /// Path to the library that you want to forget - /// - /// \return True if the library was actively loaded and is now - /// successfully forgotten. If the library was not actively loaded, this - /// returns false. - public: bool ForgetLibrary(const std::string &_pathToLibrary); - - /// \brief Forget the library that provides the plugin with the given - /// name. Note that this will also forget all other plugin types which - /// are provided by that library. - /// - /// \param[in] _pluginNameOrAlias - /// Name or alias of the plugin whose library you want to forget. - /// - /// \sa bool ForgetLibrary(const std::string &_pathToLibrary) - public: bool ForgetLibraryOfPlugin(const std::string &_pluginNameOrAlias); - - /// \brief Get a pointer to the Info corresponding to _pluginName. - /// - /// \param[in] _resolvedName - /// The resolved name, i.e. the demangled class symbol name as returned - /// by LookupPlugin(~), of the plugin that you want to instantiate. - /// - /// \return Pointer to the corresponding Info, or nullptr if there - /// is no info for the requested _pluginName. - private: ConstInfoPtr PrivateGetInfo( - const std::string &_resolvedName) const; - - /// \brief Get a std::shared_ptr that manages the lifecycle of the shared - /// library handle which provides the specified plugin. - /// - /// \param[in] _resolvedName - /// The resolved name, i.e. the demangled class symbol name as returned - /// by LookupPlugin(~), of the plugin that you want to instantiate. - /// - /// \return Reference-counting pointer to a library handle, or else a - /// nullptr if the plugin is not available. - private: std::shared_ptr PrivateGetPluginDlHandlePtr( - const std::string &_resolvedName) const; - - class Implementation; - IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING - /// \brief PIMPL pointer to class implementation - private: std::unique_ptr dataPtr; - IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING - }; - } -} - -#include - -#endif +#include diff --git a/loader/include/ignition/plugin/detail/Loader.hh b/loader/include/ignition/plugin/detail/Loader.hh index 49a293ae..428e0e71 100644 --- a/loader/include/ignition/plugin/detail/Loader.hh +++ b/loader/include/ignition/plugin/detail/Loader.hh @@ -15,52 +15,4 @@ * */ - -#ifndef IGNITION_PLUGIN_DETAIL_LOADER_HH_ -#define IGNITION_PLUGIN_DETAIL_LOADER_HH_ - -#include -#include -#include -#include -#include - -namespace ignition -{ - namespace plugin - { - template - std::unordered_set Loader::PluginsImplementing() const - { - return this->PluginsImplementing(typeid(Interface).name(), false); - } - - template - PluginPtrType Loader::Instantiate( - const std::string &_pluginNameOrAlias) const - { - const std::string &resolvedName = this->LookupPlugin(_pluginNameOrAlias); - if (resolvedName.empty()) - return PluginPtr(); - - PluginPtrType ptr(this->PrivateGetInfo(resolvedName), - this->PrivateGetPluginDlHandlePtr(resolvedName)); - - if (auto *enableFromThis = - ptr->template QueryInterface()) - enableFromThis->PrivateSetPluginFromThis(ptr); - - return ptr; - } - - template - std::shared_ptr Loader::Factory( - const std::string &_pluginNameOrAlias) const - { - return this->Instantiate(_pluginNameOrAlias) - ->template QueryInterfaceSharedPtr(); - } - } -} - -#endif +#include diff --git a/loader/include/ignition/plugin/loader/Export.hh b/loader/include/ignition/plugin/loader/Export.hh new file mode 100644 index 00000000..c4505434 --- /dev/null +++ b/loader/include/ignition/plugin/loader/Export.hh @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include diff --git a/loader/include/ignition/plugin/loader/detail/Export.hh b/loader/include/ignition/plugin/loader/detail/Export.hh new file mode 100644 index 00000000..351ef981 --- /dev/null +++ b/loader/include/ignition/plugin/loader/detail/Export.hh @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include diff --git a/register/include/gz/plugin/Register.hh b/register/include/gz/plugin/Register.hh new file mode 100644 index 00000000..ca7d8bff --- /dev/null +++ b/register/include/gz/plugin/Register.hh @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +#ifndef GZ_PLUGIN_REGISTER_HH_ +#define GZ_PLUGIN_REGISTER_HH_ + +#include + + +// ------------- Add a set of plugins or a set of interfaces ------------------ + +/// \brief Add a plugin and interface from this shared library. +/// +/// This macro can be put in any namespace and may be called any number of +/// times. It can be called multiple times on the same plugin class in order to +/// register multiple interfaces, e.g.: +/// +/// \code +/// IGNITION_ADD_PLUGIN(PluginClass, Interface1) +/// IGNITION_ADD_PLUGIN(PluginClass, Interface2) +/// +/// /* Some other code */ +/// +/// IGNITION_ADD_PLUGIN(PluginClass, Interface3) +/// \endcode +/// +/// Or you can list multiple interfaces in a single call to the macro, e.g.: +/// +/// \code +/// IGNITION_ADD_PLUGIN(PluginClass, Interface1, Interface2, Interface3) +/// \endcode +/// +/// If your library has multiple translation units (.cpp files) and you want to +/// register plugins in multiple translation units, use this +/// gz/plugin/Register.hh header in ONE of the translation units, and then +/// the gz/plugin/RegisterMore.hh header in all of the rest of the +/// translation units. +#define IGNITION_ADD_PLUGIN(PluginClass, ...) \ + DETAIL_IGNITION_ADD_PLUGIN(PluginClass, __VA_ARGS__) + +/// \brief Add an alias for one of your plugins. +/// +/// This macro can be put in any namespace and may be called any number of +/// times. It can be called multiple times on the same plugin class in order to +/// register multiple aliases, e.g.: +/// +/// \code +/// IGNITION_ADD_PLUGIN_ALIAS(PluginClass, "PluginClass") +/// IGNITION_ADD_PLUGIN_ALIAS(PluginClass, "SomeOtherName", "Yet another name") +/// IGNOTION_ADD_PLUGIN_ALIAS(AnotherPluginClass, "Foo", "Bar", "Baz") +/// \endcode +/// +/// You can give the same alias to multiple plugins, but then that alias can no +/// longer be used to instantiate any plugin. +/// +/// If you give a plugin an alias string that matches the demangled symbol name +/// of another plugin, then the Loader will always prefer to instantiate the +/// plugin whose symbol name matches that string. +#define IGNITION_ADD_PLUGIN_ALIAS(PluginClass, ...) \ + DETAIL_IGNITION_ADD_PLUGIN_ALIAS(PluginClass, __VA_ARGS__) + + +/// \brief Add a plugin factory. +/// +/// A plugin factory is a plugin that is able to generate objects that implement +/// some interface. These objects can be passed off to a consumer, and as long +/// as the object is alive, it will ensure that the shared library of the plugin +/// remains loaded. The objects are handed off with a std::unique_ptr, so the +/// raw pointer can be released from its std::unique_ptr and passed into any +/// memory management system the consumer prefers. +/// +/// The inputs and output of a factory are defined using the +/// ignition::plugin::Factory class in the gz/plugin/Factory.hh header. +/// +/// The first argument of this macro should be the class that implements the +/// factory's output interface. The second argument should be the factory +/// definition. +/// +/// NOTE: If your factory has any input arguments, then you must define it +/// outside of this macro, or else you will get a compilation error. This +/// happens because macros will parse the commas between your template arguments +/// as separators for the macro arguments. For example: +/// +/// \code +/// class MyBase +/// { +/// public: virtual double SomeFunction() = 0; +/// }; +/// +/// class MyType : public MyBase +/// { +/// public: MyType(double value); +/// public: double SomeFunction() override; +/// }; +/// +/// /* BAD! Will not compile: +/// IGNITION_ADD_FACTORY(MyType, ignition::plugin::Factory); +/// */ +/// +/// // Instead do this: +/// using MyFactory = ignition::plugin::Factory; +/// IGNITION_ADD_FACTORY(MyType, MyFactory); +/// \endcode +#define IGNITION_ADD_FACTORY(ProductType, FactoryType) \ + DETAIL_IGNITION_ADD_FACTORY(ProductType, FactoryType) + +/// \brief Add an alias for a factory. +/// +/// This will do the same as IGNITION_ADD_FACTORY(), but you may also add in +/// any number of strings which can then be used as aliases for this factory. +/// For example: +/// +/// \code +/// IGNITION_ADD_FACTORY_ALIAS(MyType, MyFactory, "Foo", "My favorite factory") +/// \endcode +/// +/// This macro can be called any number of times for the same factory or for +/// different factories. If you call this macro, you do not need to call +/// IGNITION_ADD_FACTORY(), but there is nothing wrong with calling both (except +/// it might imperceptibly increase your compile time). +#define IGNITION_ADD_FACTORY_ALIAS(ProductType, FactoryType, ...) \ + DETAIL_IGNITION_ADD_FACTORY_ALIAS(ProductType, FactoryType, __VA_ARGS__) + + +#endif diff --git a/register/include/gz/plugin/RegisterMore.hh b/register/include/gz/plugin/RegisterMore.hh new file mode 100644 index 00000000..4763420f --- /dev/null +++ b/register/include/gz/plugin/RegisterMore.hh @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +#ifndef GZ_PLUGIN_REGISTERMORE_HH_ +#define GZ_PLUGIN_REGISTERMORE_HH_ + +/// If your library already has a translation unit (.cpp file) containing +/// \code +/// #include +/// \endcode +/// +/// then any other translation units that want to register plugins should use +/// \code +/// #include +/// \endcode +/// +/// But at least one translation unit of your library must contain Register.hh. +#define IGN_PLUGIN_REGISTER_MORE_TRANS_UNITS +#include + +#endif diff --git a/register/include/gz/plugin/detail/Register.hh b/register/include/gz/plugin/detail/Register.hh new file mode 100644 index 00000000..bfffebba --- /dev/null +++ b/register/include/gz/plugin/detail/Register.hh @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2017 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +#ifndef GZ_PLUGIN_DETAIL_REGISTER_HH_ +#define GZ_PLUGIN_DETAIL_REGISTER_HH_ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef __GNUC__ + #define DETAIL_IGN_PLUGIN_VISIBLE __attribute__ ((dllexport)) + #else + #define DETAIL_IGN_PLUGIN_VISIBLE __declspec(dllexport) + #endif +#else + #if __GNUC__ >= 4 + #define DETAIL_IGN_PLUGIN_VISIBLE __attribute__ ((visibility ("default"))) + #else + #define DETAIL_IGN_PLUGIN_VISIBLE + #endif +#endif + +// extern "C" ensures that the symbol name of IgnitionPluginHook +// does not get mangled by the compiler, so we can easily use dlsym(~) to +// retrieve it. +extern "C" +{ + /// \private IgnitionPluginHook is the hook that's used by the Loader to + /// retrieve Info from a shared library that provides plugins. + /// + /// The symbol is explicitly exported (visibility is turned on) using + /// DETAIL_IGN_PLUGIN_VISIBLE to ensure that dlsym(~) is able to find it. + /// + /// DO NOT CALL THIS FUNCTION DIRECTLY OR CREATE YOUR OWN IMPLEMENTATION OF IT + /// This function is used by the Registrar and Loader classes. Nothing else + /// should be using it. + /// + /// \param[in] _inputSingleInfo + /// This argument is used by Registrar to input a single instance of + /// plugin::Info data. Loader will set this to a nullptr when trying to + /// receive data from the hook. + /// + /// \param[out] _outputAllInfo + /// Loader will pass in a pointer to a pointer of an InfoMap pertaining to + /// the highest API version that it knows of. If this IgnitionPluginHook was + /// built against a version of ign-plugin that provides an equal or greater + /// API version, then IgnitionPluginHook will modify *_outputAllInfo to + /// point at its internal &InfoMap that corresponds to the requested API + /// version, which is identified by _inputAndOutputAPIVersion. + /// + /// If _inputAndOutputAPIVersion is greater than the highest API version + /// known by this IgnitionPluginHook, then IgnitionPluginHook will not + /// modify _outputAllInfo, and instead it will change the value pointed to + /// by _inputAndOutputAPIVersion so that it points to the highest API + /// version known by this IgnitionPluginHook. At that point, Loader can call + /// this function again, but using the older API version which known by this + /// IgnitionPluginHook. + /// + /// \param[in,out] _inputAndOutputAPIVersion + /// Loader will pass in a pointer to the highest API version that it knows. + /// If that API version is higher than what this IgnitionPluginHook is + /// compatible with, then this IgnitionPluginHook will change the value + /// pointed to by _inputAndOutputAPIVersion to the value of the highest API + /// version that it knows. + /// + /// \param[in,out] _inputAndOutputInfoSize + /// This input/output parameter is used for sanity checking. The Loader + /// inputs a pointer to the size that it expects for the Info data + /// structure, and IgnitionPluginHook verifies that this expectation matches + /// its own Info size. Then, IgnitionPluginHook will overwrite the value + /// pointed to so that it matches its own Info size value. + /// + /// \param[in,out] _inputAndOutputInfoAlign + /// Similar to _inputAndOutputInfoSize, this is used for sanity checking. It + /// inspects and returns the alignof(Info) value instead of the sizeof(Info) + /// value. + DETAIL_IGN_PLUGIN_VISIBLE void IgnitionPluginHook( + const void *_inputSingleInfo, + const void ** const _outputAllInfo, + int *_inputAndOutputAPIVersion, + std::size_t *_inputAndOutputInfoSize, + std::size_t *_inputAndOutputInfoAlign) +#ifdef IGN_PLUGIN_REGISTER_MORE_TRANS_UNITS + ; /* NOLINT */ +#else + // ATTENTION: If you get a linking error complaining about + // multiple definitions of IgnitionPluginHook, + // then make sure that all but one of your + // library's translation units (.cpp files) includes the + // header instead of + // . + // + // Only ONE and exactly ONE .cpp file in your library should include + // Register.hh. All the rest should include RegisterMore.hh. It does not + // matter which .cpp file you choose, as long as it gets compiled into your + // plugin library. + // ^^^^^^^^^^^^^^^^^^^^^ READ ABOVE FOR LINKING ERRORS ^^^^^^^^^^^^^^^^^^^^^ + { + using InfoMap = ignition::plugin::InfoMap; + // We use a static variable here so that we can accumulate multiple + // Info objects from multiple plugin registration calls within one + // shared library, and then provide it all to the Loader through this + // single hook. + static InfoMap pluginMap; + + if (_inputSingleInfo) + { + // When _inputSingleInfo is not a nullptr, it means that one of the plugin + // registration macros is providing us with some Info. + const ignition::plugin::Info *input = + static_cast(_inputSingleInfo); + + InfoMap::iterator it; + bool inserted; + + // We use insert(~) to ensure that we do not accidentally overwrite some + // existing information for the plugin that has this name. + std::tie(it, inserted) = + pluginMap.insert(std::make_pair(input->name, *input)); + + if (!inserted) + { + // If the object was not inserted, then an entry already existed for + // this plugin type. We should still insert each of the interface map + // entries and aliases provided by the input info, just in case any of + // them are missing from the currently existing entry. This allows the + // user to specify different interfaces and aliases for the same plugin + // type using different macros in different locations or across multiple + // translation units. + ignition::plugin::Info &entry = it->second; + + for (const auto &interfaceMapEntry : input->interfaces) + entry.interfaces.insert(interfaceMapEntry); + + for (const auto &aliasSetEntry : input->aliases) + entry.aliases.insert(aliasSetEntry); + } + } + + if (_outputAllInfo) + { + // When _outputAllInfo is not a nullptr, it means that a Loader is + // trying to retrieve Info from us. + + // The Loader should provide valid pointers to these fields as part + // of a handshake procedure. + if (nullptr == _inputAndOutputAPIVersion || + nullptr == _inputAndOutputInfoSize || + nullptr == _inputAndOutputInfoAlign) + { + // This should never happen, or else the function is being misused. + // LCOV_EXCL_START + return; + // LCOV_EXCL_STOP + } + + bool agreement = true; + + if (ignition::plugin::INFO_API_VERSION != *_inputAndOutputAPIVersion) + { + // LCOV_EXCL_START + agreement = false; + // LCOV_EXCL_STOP + } + + if (sizeof(ignition::plugin::Info) != *_inputAndOutputInfoSize) + { + // LCOV_EXCL_START + agreement = false; + // LCOV_EXCL_STOP + } + + if (alignof(ignition::plugin::Info) != *_inputAndOutputInfoAlign) + { + // LCOV_EXCL_START + agreement = false; + // LCOV_EXCL_STOP + } + + // The handshake parameters that were passed into us are overwritten with + // the values that we have on our end. That way, if our Info API is + // lower than that of the Loader, then the Loader will know + // to call this function using an older version of Info, and then + // convert it to the newer version on the loader side. + // + // This implementation might change when new API versions are introduced, + // but this current implementation will still be forward compatible with + // new API versions. + *_inputAndOutputAPIVersion = ignition::plugin::INFO_API_VERSION; + *_inputAndOutputInfoSize = sizeof(ignition::plugin::Info); + *_inputAndOutputInfoAlign = alignof(ignition::plugin::Info); + + // If the size, alignment, or API do not agree, we should return without + // outputting any of the plugin info; otherwise, we could get a + // segmentation fault. + // + // We will return the current API version to the Loader, and it may + // then decide to attempt the call to this function again with the correct + // API version if it supports backwards/forwards compatibility. + if (!agreement) + { + // LCOV_EXCL_START + return; + // LCOV_EXCL_STOP + } + + *_outputAllInfo = &pluginMap; + } + } +#endif +} + +namespace ignition +{ + namespace plugin + { + namespace detail + { + ////////////////////////////////////////////////// + /// \brief This default will be called when NoMoreInterfaces is an empty + /// parameter pack. When one or more Interfaces are provided, the other + /// template specialization of this class will be called. + template + struct InterfaceHelper + { + public: static void InsertInterfaces(Info::InterfaceCastingMap &) + { + // Do nothing. This is the terminal specialization of the variadic + // template class member function. + } + }; + + ////////////////////////////////////////////////// + /// \brief This specialization will be called when one or more Interfaces + /// are specified. + template + struct InterfaceHelper + { + public: static void InsertInterfaces( + Info::InterfaceCastingMap &interfaces) + { + // READ ME: If you get a compilation error here, then one of the + // interfaces that you tried to register for your plugin is not + // actually a base class of the plugin class. This is not allowed. A + // plugin class must inherit every interface class that you want it to + // provide. + static_assert(std::is_base_of::value, + "YOU ARE ATTEMPTING TO REGISTER AN INTERFACE FOR A " + "PLUGIN, BUT THE INTERFACE IS NOT A BASE CLASS OF THE " + "PLUGIN."); + + interfaces.insert(std::make_pair( + typeid(Interface).name(), + [=](void* v_ptr) + { + PluginClass *d_ptr = static_cast(v_ptr); + return static_cast(d_ptr); + })); + + InterfaceHelper + ::InsertInterfaces(interfaces); + } + }; + + ////////////////////////////////////////////////// + /// \brief This overload will be called when no more aliases remain to be + /// inserted. If one or more aliases still need to be inserted, then the + /// overload below this one will be called instead. + inline void InsertAlias(std::set &/*aliases*/) + { + // Do nothing. This is the terminal overload of the variadic template + // function. + } + + template + void InsertAlias(std::set &aliases, + const std::string &nextAlias, + Aliases&&... remainingAliases) + { + aliases.insert(nextAlias); + InsertAlias(aliases, std::forward(remainingAliases)...); + } + + ////////////////////////////////////////////////// + template + struct IfEnablePluginFromThisImpl + { + public: static void AddIt(Info::InterfaceCastingMap &_interfaces) + { + _interfaces.insert(std::make_pair( + typeid(EnablePluginFromThis).name(), + [=](void *v_ptr) + { + PluginClass *d_ptr = static_cast(v_ptr); + return static_cast(d_ptr); + })); + } + }; + + ////////////////////////////////////////////////// + template + struct IfEnablePluginFromThisImpl + { + public: static void AddIt(Info::InterfaceCastingMap &) + { + // Do nothing, because the plugin does not inherit + // the EnablePluginFromThis interface. + } + }; + + ////////////////////////////////////////////////// + template + struct IfEnablePluginFromThis + : IfEnablePluginFromThisImpl::value> + { }; // NOLINT + + ////////////////////////////////////////////////// + /// \brief This specialization of the Register class will be called when + /// one or more arguments are provided to the IGNITION_ADD_PLUGIN(~) + /// macro. This is the only version of the Registrar class that is allowed + /// to compile. + template + struct Registrar + { + public: static Info MakeInfo() + { + Info info; + + // Set the name of the plugin + info.name = typeid(PluginClass).name(); + + // Create a factory for generating new plugin instances + info.factory = [=]() + { + // vvvvvvvvvvvvvvvvvvvvvvvv READ ME vvvvvvvvvvvvvvvvvvvvvvvvvvvvv + // If you get a compilation error here, then you are trying to + // register an abstract class as a plugin, which is not allowed. To + // register a plugin class, every one if its virtual functions must + // have a definition. + // + // Read through the error produced by your compiler to see which + // pure virtual functions you are neglecting to provide overrides + // for. + // ^^^^^^^^^^^^^ READ ABOVE FOR COMPILATION ERRORS ^^^^^^^^^^^^^^^^ + return static_cast(new PluginClass); + }; + +IGN_UTILS_WARN_IGNORE__NON_VIRTUAL_DESTRUCTOR + // Create a deleter to clean up destroyed instances + info.deleter = [=](void *ptr) + { + delete static_cast(ptr); + }; +IGN_UTILS_WARN_RESUME__NON_VIRTUAL_DESTRUCTOR + + // Construct a map from the plugin to its interfaces + InterfaceHelper + ::InsertInterfaces(info.interfaces); + + return info; + } + + /// \brief This function registers a plugin along with a set of + /// interfaces that it provides. + public: static void Register() + { + // Make all info that the user has specified + Info info = MakeInfo(); + + // Add the EnablePluginFromThis interface automatically if it is + // inherited by PluginClass. + IfEnablePluginFromThis::AddIt(info.interfaces); + + // Send this information as input to this library's global repository + // of plugins. + IgnitionPluginHook(&info, nullptr, nullptr, nullptr, nullptr); + } + + + public: template + static void RegisterAlias(Aliases&&... aliases) + { + // Dev note (MXG): We expect the RegisterAlias function to be called + // using the IGNITION_ADD_PLUGIN_ALIAS(~) macro, which should never + // contain any interfaces. Therefore, this parameter pack should be + // empty. + // + // In the future, we could allow Interfaces and Aliases to be + // specified simultaneously, but that would be very tricky to do with + // macros, so for now we will enforce this assumption to make sure + // that the implementation is working as expected. + static_assert(sizeof...(Interfaces) == 0, + "THERE IS A BUG IN THE ALIAS REGISTRATION " + "IMPLEMENTATION! PLEASE REPORT THIS!"); + + Info info = MakeInfo(); + + // Gather up all the aliases that have been specified for this plugin. + InsertAlias(info.aliases, std::forward(aliases)...); + + // Send this information as input to this library's global repository + // of plugins. + IgnitionPluginHook(&info, nullptr, nullptr, nullptr, nullptr); + } + }; + } + } +} + +////////////////////////////////////////////////// +/// This macro creates a uniquely-named class whose constructor calls the +/// ignition::plugin::detail::Registrar::Register function. It then declares a +/// uniquely-named instance of the class with static lifetime. Since the class +/// instance has a static lifetime, it will be constructed when the shared +/// library is loaded. When it is constructed, the Register function will +/// be called. +#define DETAIL_IGNITION_ADD_PLUGIN_HELPER(UniqueID, ...) \ + namespace ignition \ + { \ + namespace plugin \ + { \ + namespace \ + { \ + struct ExecuteWhenLoadingLibrary##UniqueID \ + { \ + ExecuteWhenLoadingLibrary##UniqueID() \ + { \ + ::ignition::plugin::detail::Registrar<__VA_ARGS__>::Register(); \ + } \ + }; \ + \ + static ExecuteWhenLoadingLibrary##UniqueID execute##UniqueID; \ + } /* namespace */ \ + } \ + } + + +////////////////////////////////////////////////// +/// This macro is needed to force the __COUNTER__ macro to expand to a value +/// before being passed to the *_HELPER macro. +#define DETAIL_IGNITION_ADD_PLUGIN_WITH_COUNTER(UniqueID, ...) \ + DETAIL_IGNITION_ADD_PLUGIN_HELPER(UniqueID, __VA_ARGS__) + + +////////////////////////////////////////////////// +/// We use the __COUNTER__ here to give each plugin registration its own unique +/// name, which is required in order to statically initialize each one. +#define DETAIL_IGNITION_ADD_PLUGIN(...) \ + DETAIL_IGNITION_ADD_PLUGIN_WITH_COUNTER(__COUNTER__, __VA_ARGS__) + + +////////////////////////////////////////////////// +/// This macro creates a uniquely-named class whose constructor calls the +/// ignition::plugin::detail::Registrar::RegisterAlias function. It then +/// declares a uniquely-named instance of the class with static lifetime. Since +/// the class instance has a static lifetime, it will be constructed when the +/// shared library is loaded. When it is constructed, the Register function will +/// be called. +#define DETAIL_IGNITION_ADD_PLUGIN_ALIAS_HELPER(UniqueID, PluginClass, ...) \ + namespace ignition \ + { \ + namespace plugin \ + { \ + namespace \ + { \ + struct ExecuteWhenLoadingLibrary##UniqueID \ + { \ + ExecuteWhenLoadingLibrary##UniqueID() \ + { \ + ::ignition::plugin::detail::Registrar::RegisterAlias( \ + __VA_ARGS__); \ + } \ + }; \ + \ + static ExecuteWhenLoadingLibrary##UniqueID execute##UniqueID; \ + } /* namespace */ \ + } \ + } + + +////////////////////////////////////////////////// +/// This macro is needed to force the __COUNTER__ macro to expand to a value +/// before being passed to the *_HELPER macro. +#define DETAIL_IGNITION_ADD_PLUGIN_ALIAS_WITH_COUNTER( \ + UniqueID, PluginClass, ...) \ + DETAIL_IGNITION_ADD_PLUGIN_ALIAS_HELPER(UniqueID, PluginClass, __VA_ARGS__) + + +////////////////////////////////////////////////// +/// We use the __COUNTER__ here to give each plugin registration its own unique +/// name, which is required in order to statically initialize each one. +#define DETAIL_IGNITION_ADD_PLUGIN_ALIAS(PluginClass, ...) \ + DETAIL_IGNITION_ADD_PLUGIN_ALIAS_WITH_COUNTER( \ + __COUNTER__, PluginClass, __VA_ARGS__) + + +////////////////////////////////////////////////// +#define DETAIL_IGNITION_ADD_FACTORY(ProductType, FactoryType) \ + DETAIL_IGNITION_ADD_PLUGIN(FactoryType::Producing, FactoryType) \ + DETAIL_IGNITION_ADD_PLUGIN_ALIAS( \ + FactoryType::Producing, \ + ::ignition::plugin::DemangleSymbol(typeid(ProductType).name())) + + +////////////////////////////////////////////////// +#define DETAIL_IGNITION_ADD_FACTORY_ALIAS(ProductType, FactoryType, ...) \ + DETAIL_IGNITION_ADD_FACTORY(ProductType, FactoryType) \ + DETAIL_IGNITION_ADD_PLUGIN_ALIAS(FactoryType::Producing, \ + __VA_ARGS__) + +#endif diff --git a/register/include/ignition/plugin/Register.hh b/register/include/ignition/plugin/Register.hh index e0cb43ad..7a3d2926 100644 --- a/register/include/ignition/plugin/Register.hh +++ b/register/include/ignition/plugin/Register.hh @@ -13,128 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ - -#ifndef IGNITION_PLUGIN_REGISTER_HH_ -#define IGNITION_PLUGIN_REGISTER_HH_ - -#include - - -// ------------- Add a set of plugins or a set of interfaces ------------------ - -/// \brief Add a plugin and interface from this shared library. -/// -/// This macro can be put in any namespace and may be called any number of -/// times. It can be called multiple times on the same plugin class in order to -/// register multiple interfaces, e.g.: -/// -/// \code -/// IGNITION_ADD_PLUGIN(PluginClass, Interface1) -/// IGNITION_ADD_PLUGIN(PluginClass, Interface2) -/// -/// /* Some other code */ -/// -/// IGNITION_ADD_PLUGIN(PluginClass, Interface3) -/// \endcode -/// -/// Or you can list multiple interfaces in a single call to the macro, e.g.: -/// -/// \code -/// IGNITION_ADD_PLUGIN(PluginClass, Interface1, Interface2, Interface3) -/// \endcode -/// -/// If your library has multiple translation units (.cpp files) and you want to -/// register plugins in multiple translation units, use this -/// ignition/plugin/Register.hh header in ONE of the translation units, and then -/// the ignition/plugin/RegisterMore.hh header in all of the rest of the -/// translation units. -#define IGNITION_ADD_PLUGIN(PluginClass, ...) \ - DETAIL_IGNITION_ADD_PLUGIN(PluginClass, __VA_ARGS__) - -/// \brief Add an alias for one of your plugins. -/// -/// This macro can be put in any namespace and may be called any number of -/// times. It can be called multiple times on the same plugin class in order to -/// register multiple aliases, e.g.: -/// -/// \code -/// IGNITION_ADD_PLUGIN_ALIAS(PluginClass, "PluginClass") -/// IGNITION_ADD_PLUGIN_ALIAS(PluginClass, "SomeOtherName", "Yet another name") -/// IGNOTION_ADD_PLUGIN_ALIAS(AnotherPluginClass, "Foo", "Bar", "Baz") -/// \endcode -/// -/// You can give the same alias to multiple plugins, but then that alias can no -/// longer be used to instantiate any plugin. -/// -/// If you give a plugin an alias string that matches the demangled symbol name -/// of another plugin, then the Loader will always prefer to instantiate the -/// plugin whose symbol name matches that string. -#define IGNITION_ADD_PLUGIN_ALIAS(PluginClass, ...) \ - DETAIL_IGNITION_ADD_PLUGIN_ALIAS(PluginClass, __VA_ARGS__) - - -/// \brief Add a plugin factory. -/// -/// A plugin factory is a plugin that is able to generate objects that implement -/// some interface. These objects can be passed off to a consumer, and as long -/// as the object is alive, it will ensure that the shared library of the plugin -/// remains loaded. The objects are handed off with a std::unique_ptr, so the -/// raw pointer can be released from its std::unique_ptr and passed into any -/// memory management system the consumer prefers. -/// -/// The inputs and output of a factory are defined using the -/// ignition::plugin::Factory class in the ignition/plugin/Factory.hh header. -/// -/// The first argument of this macro should be the class that implements the -/// factory's output interface. The second argument should be the factory -/// definition. -/// -/// NOTE: If your factory has any input arguments, then you must define it -/// outside of this macro, or else you will get a compilation error. This -/// happens because macros will parse the commas between your template arguments -/// as separators for the macro arguments. For example: -/// -/// \code -/// class MyBase -/// { -/// public: virtual double SomeFunction() = 0; -/// }; -/// -/// class MyType : public MyBase -/// { -/// public: MyType(double value); -/// public: double SomeFunction() override; -/// }; -/// -/// /* BAD! Will not compile: -/// IGNITION_ADD_FACTORY(MyType, ignition::plugin::Factory); -/// */ -/// -/// // Instead do this: -/// using MyFactory = ignition::plugin::Factory; -/// IGNITION_ADD_FACTORY(MyType, MyFactory); -/// \endcode -#define IGNITION_ADD_FACTORY(ProductType, FactoryType) \ - DETAIL_IGNITION_ADD_FACTORY(ProductType, FactoryType) - -/// \brief Add an alias for a factory. -/// -/// This will do the same as IGNITION_ADD_FACTORY(), but you may also add in -/// any number of strings which can then be used as aliases for this factory. -/// For example: -/// -/// \code -/// IGNITION_ADD_FACTORY_ALIAS(MyType, MyFactory, "Foo", "My favorite factory") -/// \endcode -/// -/// This macro can be called any number of times for the same factory or for -/// different factories. If you call this macro, you do not need to call -/// IGNITION_ADD_FACTORY(), but there is nothing wrong with calling both (except -/// it might imperceptibly increase your compile time). -#define IGNITION_ADD_FACTORY_ALIAS(ProductType, FactoryType, ...) \ - DETAIL_IGNITION_ADD_FACTORY_ALIAS(ProductType, FactoryType, __VA_ARGS__) - - -#endif +#include diff --git a/register/include/ignition/plugin/RegisterMore.hh b/register/include/ignition/plugin/RegisterMore.hh index a69d6d8b..1fe8663e 100644 --- a/register/include/ignition/plugin/RegisterMore.hh +++ b/register/include/ignition/plugin/RegisterMore.hh @@ -13,24 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ - -#ifndef IGNITION_PLUGIN_REGISTERMORE_HH_ -#define IGNITION_PLUGIN_REGISTERMORE_HH_ - -/// If your library already has a translation unit (.cpp file) containing -/// \code -/// #include -/// \endcode -/// -/// then any other translation units that want to register plugins should use -/// \code -/// #include -/// \endcode -/// -/// But at least one translation unit of your library must contain Register.hh. -#define IGN_PLUGIN_REGISTER_MORE_TRANS_UNITS -#include - -#endif +#include diff --git a/register/include/ignition/plugin/detail/Register.hh b/register/include/ignition/plugin/detail/Register.hh index 021d93b3..1e1ead2f 100644 --- a/register/include/ignition/plugin/detail/Register.hh +++ b/register/include/ignition/plugin/detail/Register.hh @@ -13,527 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ - -#ifndef IGNITION_PLUGIN_DETAIL_REGISTER_HH_ -#define IGNITION_PLUGIN_DETAIL_REGISTER_HH_ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - - -#if defined _WIN32 || defined __CYGWIN__ - #ifdef __GNUC__ - #define DETAIL_IGN_PLUGIN_VISIBLE __attribute__ ((dllexport)) - #else - #define DETAIL_IGN_PLUGIN_VISIBLE __declspec(dllexport) - #endif -#else - #if __GNUC__ >= 4 - #define DETAIL_IGN_PLUGIN_VISIBLE __attribute__ ((visibility ("default"))) - #else - #define DETAIL_IGN_PLUGIN_VISIBLE - #endif -#endif - -// extern "C" ensures that the symbol name of IgnitionPluginHook -// does not get mangled by the compiler, so we can easily use dlsym(~) to -// retrieve it. -extern "C" -{ - /// \private IgnitionPluginHook is the hook that's used by the Loader to - /// retrieve Info from a shared library that provides plugins. - /// - /// The symbol is explicitly exported (visibility is turned on) using - /// DETAIL_IGN_PLUGIN_VISIBLE to ensure that dlsym(~) is able to find it. - /// - /// DO NOT CALL THIS FUNCTION DIRECTLY OR CREATE YOUR OWN IMPLEMENTATION OF IT - /// This function is used by the Registrar and Loader classes. Nothing else - /// should be using it. - /// - /// \param[in] _inputSingleInfo - /// This argument is used by Registrar to input a single instance of - /// plugin::Info data. Loader will set this to a nullptr when trying to - /// receive data from the hook. - /// - /// \param[out] _outputAllInfo - /// Loader will pass in a pointer to a pointer of an InfoMap pertaining to - /// the highest API version that it knows of. If this IgnitionPluginHook was - /// built against a version of ign-plugin that provides an equal or greater - /// API version, then IgnitionPluginHook will modify *_outputAllInfo to - /// point at its internal &InfoMap that corresponds to the requested API - /// version, which is identified by _inputAndOutputAPIVersion. - /// - /// If _inputAndOutputAPIVersion is greater than the highest API version - /// known by this IgnitionPluginHook, then IgnitionPluginHook will not - /// modify _outputAllInfo, and instead it will change the value pointed to - /// by _inputAndOutputAPIVersion so that it points to the highest API - /// version known by this IgnitionPluginHook. At that point, Loader can call - /// this function again, but using the older API version which known by this - /// IgnitionPluginHook. - /// - /// \param[in,out] _inputAndOutputAPIVersion - /// Loader will pass in a pointer to the highest API version that it knows. - /// If that API version is higher than what this IgnitionPluginHook is - /// compatible with, then this IgnitionPluginHook will change the value - /// pointed to by _inputAndOutputAPIVersion to the value of the highest API - /// version that it knows. - /// - /// \param[in,out] _inputAndOutputInfoSize - /// This input/output parameter is used for sanity checking. The Loader - /// inputs a pointer to the size that it expects for the Info data - /// structure, and IgnitionPluginHook verifies that this expectation matches - /// its own Info size. Then, IgnitionPluginHook will overwrite the value - /// pointed to so that it matches its own Info size value. - /// - /// \param[in,out] _inputAndOutputInfoAlign - /// Similar to _inputAndOutputInfoSize, this is used for sanity checking. It - /// inspects and returns the alignof(Info) value instead of the sizeof(Info) - /// value. - DETAIL_IGN_PLUGIN_VISIBLE void IgnitionPluginHook( - const void *_inputSingleInfo, - const void ** const _outputAllInfo, - int *_inputAndOutputAPIVersion, - std::size_t *_inputAndOutputInfoSize, - std::size_t *_inputAndOutputInfoAlign) -#ifdef IGN_PLUGIN_REGISTER_MORE_TRANS_UNITS - ; /* NOLINT */ -#else - // ATTENTION: If you get a linking error complaining about - // multiple definitions of IgnitionPluginHook, - // then make sure that all but one of your - // library's translation units (.cpp files) includes the - // header instead of - // . - // - // Only ONE and exactly ONE .cpp file in your library should include - // Register.hh. All the rest should include RegisterMore.hh. It does not - // matter which .cpp file you choose, as long as it gets compiled into your - // plugin library. - // ^^^^^^^^^^^^^^^^^^^^^ READ ABOVE FOR LINKING ERRORS ^^^^^^^^^^^^^^^^^^^^^ - { - using InfoMap = ignition::plugin::InfoMap; - // We use a static variable here so that we can accumulate multiple - // Info objects from multiple plugin registration calls within one - // shared library, and then provide it all to the Loader through this - // single hook. - static InfoMap pluginMap; - - if (_inputSingleInfo) - { - // When _inputSingleInfo is not a nullptr, it means that one of the plugin - // registration macros is providing us with some Info. - const ignition::plugin::Info *input = - static_cast(_inputSingleInfo); - - InfoMap::iterator it; - bool inserted; - - // We use insert(~) to ensure that we do not accidentally overwrite some - // existing information for the plugin that has this name. - std::tie(it, inserted) = - pluginMap.insert(std::make_pair(input->name, *input)); - - if (!inserted) - { - // If the object was not inserted, then an entry already existed for - // this plugin type. We should still insert each of the interface map - // entries and aliases provided by the input info, just in case any of - // them are missing from the currently existing entry. This allows the - // user to specify different interfaces and aliases for the same plugin - // type using different macros in different locations or across multiple - // translation units. - ignition::plugin::Info &entry = it->second; - - for (const auto &interfaceMapEntry : input->interfaces) - entry.interfaces.insert(interfaceMapEntry); - - for (const auto &aliasSetEntry : input->aliases) - entry.aliases.insert(aliasSetEntry); - } - } - - if (_outputAllInfo) - { - // When _outputAllInfo is not a nullptr, it means that a Loader is - // trying to retrieve Info from us. - - // The Loader should provide valid pointers to these fields as part - // of a handshake procedure. - if (nullptr == _inputAndOutputAPIVersion || - nullptr == _inputAndOutputInfoSize || - nullptr == _inputAndOutputInfoAlign) - { - // This should never happen, or else the function is being misused. - // LCOV_EXCL_START - return; - // LCOV_EXCL_STOP - } - - bool agreement = true; - - if (ignition::plugin::INFO_API_VERSION != *_inputAndOutputAPIVersion) - { - // LCOV_EXCL_START - agreement = false; - // LCOV_EXCL_STOP - } - - if (sizeof(ignition::plugin::Info) != *_inputAndOutputInfoSize) - { - // LCOV_EXCL_START - agreement = false; - // LCOV_EXCL_STOP - } - - if (alignof(ignition::plugin::Info) != *_inputAndOutputInfoAlign) - { - // LCOV_EXCL_START - agreement = false; - // LCOV_EXCL_STOP - } - - // The handshake parameters that were passed into us are overwritten with - // the values that we have on our end. That way, if our Info API is - // lower than that of the Loader, then the Loader will know - // to call this function using an older version of Info, and then - // convert it to the newer version on the loader side. - // - // This implementation might change when new API versions are introduced, - // but this current implementation will still be forward compatible with - // new API versions. - *_inputAndOutputAPIVersion = ignition::plugin::INFO_API_VERSION; - *_inputAndOutputInfoSize = sizeof(ignition::plugin::Info); - *_inputAndOutputInfoAlign = alignof(ignition::plugin::Info); - - // If the size, alignment, or API do not agree, we should return without - // outputting any of the plugin info; otherwise, we could get a - // segmentation fault. - // - // We will return the current API version to the Loader, and it may - // then decide to attempt the call to this function again with the correct - // API version if it supports backwards/forwards compatibility. - if (!agreement) - { - // LCOV_EXCL_START - return; - // LCOV_EXCL_STOP - } - - *_outputAllInfo = &pluginMap; - } - } -#endif -} - -namespace ignition -{ - namespace plugin - { - namespace detail - { - ////////////////////////////////////////////////// - /// \brief This default will be called when NoMoreInterfaces is an empty - /// parameter pack. When one or more Interfaces are provided, the other - /// template specialization of this class will be called. - template - struct InterfaceHelper - { - public: static void InsertInterfaces(Info::InterfaceCastingMap &) - { - // Do nothing. This is the terminal specialization of the variadic - // template class member function. - } - }; - - ////////////////////////////////////////////////// - /// \brief This specialization will be called when one or more Interfaces - /// are specified. - template - struct InterfaceHelper - { - public: static void InsertInterfaces( - Info::InterfaceCastingMap &interfaces) - { - // READ ME: If you get a compilation error here, then one of the - // interfaces that you tried to register for your plugin is not - // actually a base class of the plugin class. This is not allowed. A - // plugin class must inherit every interface class that you want it to - // provide. - static_assert(std::is_base_of::value, - "YOU ARE ATTEMPTING TO REGISTER AN INTERFACE FOR A " - "PLUGIN, BUT THE INTERFACE IS NOT A BASE CLASS OF THE " - "PLUGIN."); - - interfaces.insert(std::make_pair( - typeid(Interface).name(), - [=](void* v_ptr) - { - PluginClass *d_ptr = static_cast(v_ptr); - return static_cast(d_ptr); - })); - - InterfaceHelper - ::InsertInterfaces(interfaces); - } - }; - - ////////////////////////////////////////////////// - /// \brief This overload will be called when no more aliases remain to be - /// inserted. If one or more aliases still need to be inserted, then the - /// overload below this one will be called instead. - inline void InsertAlias(std::set &/*aliases*/) - { - // Do nothing. This is the terminal overload of the variadic template - // function. - } - - template - void InsertAlias(std::set &aliases, - const std::string &nextAlias, - Aliases&&... remainingAliases) - { - aliases.insert(nextAlias); - InsertAlias(aliases, std::forward(remainingAliases)...); - } - - ////////////////////////////////////////////////// - template - struct IfEnablePluginFromThisImpl - { - public: static void AddIt(Info::InterfaceCastingMap &_interfaces) - { - _interfaces.insert(std::make_pair( - typeid(EnablePluginFromThis).name(), - [=](void *v_ptr) - { - PluginClass *d_ptr = static_cast(v_ptr); - return static_cast(d_ptr); - })); - } - }; - - ////////////////////////////////////////////////// - template - struct IfEnablePluginFromThisImpl - { - public: static void AddIt(Info::InterfaceCastingMap &) - { - // Do nothing, because the plugin does not inherit - // the EnablePluginFromThis interface. - } - }; - - ////////////////////////////////////////////////// - template - struct IfEnablePluginFromThis - : IfEnablePluginFromThisImpl::value> - { }; // NOLINT - - ////////////////////////////////////////////////// - /// \brief This specialization of the Register class will be called when - /// one or more arguments are provided to the IGNITION_ADD_PLUGIN(~) - /// macro. This is the only version of the Registrar class that is allowed - /// to compile. - template - struct Registrar - { - public: static Info MakeInfo() - { - Info info; - - // Set the name of the plugin - info.name = typeid(PluginClass).name(); - - // Create a factory for generating new plugin instances - info.factory = [=]() - { - // vvvvvvvvvvvvvvvvvvvvvvvv READ ME vvvvvvvvvvvvvvvvvvvvvvvvvvvvv - // If you get a compilation error here, then you are trying to - // register an abstract class as a plugin, which is not allowed. To - // register a plugin class, every one if its virtual functions must - // have a definition. - // - // Read through the error produced by your compiler to see which - // pure virtual functions you are neglecting to provide overrides - // for. - // ^^^^^^^^^^^^^ READ ABOVE FOR COMPILATION ERRORS ^^^^^^^^^^^^^^^^ - return static_cast(new PluginClass); - }; - -IGN_UTILS_WARN_IGNORE__NON_VIRTUAL_DESTRUCTOR - // Create a deleter to clean up destroyed instances - info.deleter = [=](void *ptr) - { - delete static_cast(ptr); - }; -IGN_UTILS_WARN_RESUME__NON_VIRTUAL_DESTRUCTOR - - // Construct a map from the plugin to its interfaces - InterfaceHelper - ::InsertInterfaces(info.interfaces); - - return info; - } - - /// \brief This function registers a plugin along with a set of - /// interfaces that it provides. - public: static void Register() - { - // Make all info that the user has specified - Info info = MakeInfo(); - - // Add the EnablePluginFromThis interface automatically if it is - // inherited by PluginClass. - IfEnablePluginFromThis::AddIt(info.interfaces); - - // Send this information as input to this library's global repository - // of plugins. - IgnitionPluginHook(&info, nullptr, nullptr, nullptr, nullptr); - } - - - public: template - static void RegisterAlias(Aliases&&... aliases) - { - // Dev note (MXG): We expect the RegisterAlias function to be called - // using the IGNITION_ADD_PLUGIN_ALIAS(~) macro, which should never - // contain any interfaces. Therefore, this parameter pack should be - // empty. - // - // In the future, we could allow Interfaces and Aliases to be - // specified simultaneously, but that would be very tricky to do with - // macros, so for now we will enforce this assumption to make sure - // that the implementation is working as expected. - static_assert(sizeof...(Interfaces) == 0, - "THERE IS A BUG IN THE ALIAS REGISTRATION " - "IMPLEMENTATION! PLEASE REPORT THIS!"); - - Info info = MakeInfo(); - - // Gather up all the aliases that have been specified for this plugin. - InsertAlias(info.aliases, std::forward(aliases)...); - - // Send this information as input to this library's global repository - // of plugins. - IgnitionPluginHook(&info, nullptr, nullptr, nullptr, nullptr); - } - }; - } - } -} - -////////////////////////////////////////////////// -/// This macro creates a uniquely-named class whose constructor calls the -/// ignition::plugin::detail::Registrar::Register function. It then declares a -/// uniquely-named instance of the class with static lifetime. Since the class -/// instance has a static lifetime, it will be constructed when the shared -/// library is loaded. When it is constructed, the Register function will -/// be called. -#define DETAIL_IGNITION_ADD_PLUGIN_HELPER(UniqueID, ...) \ - namespace ignition \ - { \ - namespace plugin \ - { \ - namespace \ - { \ - struct ExecuteWhenLoadingLibrary##UniqueID \ - { \ - ExecuteWhenLoadingLibrary##UniqueID() \ - { \ - ::ignition::plugin::detail::Registrar<__VA_ARGS__>::Register(); \ - } \ - }; \ - \ - static ExecuteWhenLoadingLibrary##UniqueID execute##UniqueID; \ - } /* namespace */ \ - } \ - } - - -////////////////////////////////////////////////// -/// This macro is needed to force the __COUNTER__ macro to expand to a value -/// before being passed to the *_HELPER macro. -#define DETAIL_IGNITION_ADD_PLUGIN_WITH_COUNTER(UniqueID, ...) \ - DETAIL_IGNITION_ADD_PLUGIN_HELPER(UniqueID, __VA_ARGS__) - - -////////////////////////////////////////////////// -/// We use the __COUNTER__ here to give each plugin registration its own unique -/// name, which is required in order to statically initialize each one. -#define DETAIL_IGNITION_ADD_PLUGIN(...) \ - DETAIL_IGNITION_ADD_PLUGIN_WITH_COUNTER(__COUNTER__, __VA_ARGS__) - - -////////////////////////////////////////////////// -/// This macro creates a uniquely-named class whose constructor calls the -/// ignition::plugin::detail::Registrar::RegisterAlias function. It then -/// declares a uniquely-named instance of the class with static lifetime. Since -/// the class instance has a static lifetime, it will be constructed when the -/// shared library is loaded. When it is constructed, the Register function will -/// be called. -#define DETAIL_IGNITION_ADD_PLUGIN_ALIAS_HELPER(UniqueID, PluginClass, ...) \ - namespace ignition \ - { \ - namespace plugin \ - { \ - namespace \ - { \ - struct ExecuteWhenLoadingLibrary##UniqueID \ - { \ - ExecuteWhenLoadingLibrary##UniqueID() \ - { \ - ::ignition::plugin::detail::Registrar::RegisterAlias( \ - __VA_ARGS__); \ - } \ - }; \ - \ - static ExecuteWhenLoadingLibrary##UniqueID execute##UniqueID; \ - } /* namespace */ \ - } \ - } - - -////////////////////////////////////////////////// -/// This macro is needed to force the __COUNTER__ macro to expand to a value -/// before being passed to the *_HELPER macro. -#define DETAIL_IGNITION_ADD_PLUGIN_ALIAS_WITH_COUNTER( \ - UniqueID, PluginClass, ...) \ - DETAIL_IGNITION_ADD_PLUGIN_ALIAS_HELPER(UniqueID, PluginClass, __VA_ARGS__) - - -////////////////////////////////////////////////// -/// We use the __COUNTER__ here to give each plugin registration its own unique -/// name, which is required in order to statically initialize each one. -#define DETAIL_IGNITION_ADD_PLUGIN_ALIAS(PluginClass, ...) \ - DETAIL_IGNITION_ADD_PLUGIN_ALIAS_WITH_COUNTER( \ - __COUNTER__, PluginClass, __VA_ARGS__) - - -////////////////////////////////////////////////// -#define DETAIL_IGNITION_ADD_FACTORY(ProductType, FactoryType) \ - DETAIL_IGNITION_ADD_PLUGIN(FactoryType::Producing, FactoryType) \ - DETAIL_IGNITION_ADD_PLUGIN_ALIAS( \ - FactoryType::Producing, \ - ::ignition::plugin::DemangleSymbol(typeid(ProductType).name())) - - -////////////////////////////////////////////////// -#define DETAIL_IGNITION_ADD_FACTORY_ALIAS(ProductType, FactoryType, ...) \ - DETAIL_IGNITION_ADD_FACTORY(ProductType, FactoryType) \ - DETAIL_IGNITION_ADD_PLUGIN_ALIAS(FactoryType::Producing, \ - __VA_ARGS__) - -#endif +#include