Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ign -> gz Migrate Ignition Headers : gz-plugin #101

Merged
merged 4 commits into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ set(IGN_CMAKE_VER ${ignition-cmake2_VERSION_MAJOR})
#============================================================================
# Configure the project
#============================================================================
ign_configure_project(VERSION_SUFFIX)
ign_configure_project(
REPLACE_IGNITION_INCLUDE_PATH gz/plugin
VERSION_SUFFIX
)

#============================================================================
# Set project-specific options
Expand Down Expand Up @@ -71,4 +74,3 @@ ign_create_docs(
API_MAINPAGE_MD "${CMAKE_BINARY_DIR}/api.md"
TUTORIALS_MAINPAGE_MD "${CMAKE_BINARY_DIR}/tutorials.md"
)

7 changes: 3 additions & 4 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ simply need to make sure that the compiler can resolve the names of the classes
that you pass to it (and there will be a compilation error if it cannot).

It is now possible to register plugins across **multiple translation units**
within a single library. To do this, use `#include <ignition/plugin/Register.hh>`
within a single library. To do this, use `#include <gz/plugin/Register.hh>`
in **exactly one** of your library's translation units, and then use
`#include <ignition/plugin/RegisterMore.hh>` in all other translation units. It
`#include <gz/plugin/RegisterMore.hh>` in all other translation units. It
does not matter which translation unit you choose to be the "first", as long as
you choose exactly one.

Expand All @@ -84,7 +84,7 @@ then you should continue to use it. It does not have a replacement in `ign-plugi

Here is a list of things that you *should* replace:

* `#include <ignition/common/PluginLoader.hh>` should be replaced with `#include <ignition/plugin/Loader.hh>`
* `#include <gz/common/PluginLoader.hh>` should be replaced with `#include <gz/plugin/Loader.hh>`
* `ignition::common::PluginLoader` should be replaced with `ignition::plugin::Loader`
* When calling `Loader::Instantiate("....")` do **NOT** prefix the class name with `::`. E.g. `"::some_namespace::MyClass"` should now be `"some_namespace::MyClass"`.

Expand Down Expand Up @@ -164,4 +164,3 @@ In general, plugin names that get passed to a `Loader` should not be hard-coded.
They should be selected by either inspecting what interfaces they provide, or by
having a user specify the plugin name. This rule of thumb applies to both
template-based classes and to regular classes.

3 changes: 1 addition & 2 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ ign_build_tests(
TYPE UNIT
SOURCES ${tests})


add_subdirectory(include/ignition/plugin)
add_subdirectory(include)
2 changes: 2 additions & 0 deletions core/include/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory(gz/plugin)
install(DIRECTORY ignition DESTINATION ${IGN_INCLUDE_INSTALL_DIR_FULL})
86 changes: 86 additions & 0 deletions core/include/gz/plugin/EnablePluginFromThis.hh
Original file line number Diff line number Diff line change
@@ -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 <memory>

#include <gz/plugin/PluginPtr.hh>

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<EnablePluginFromThis>()`
///
/// \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<void> 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<Implementation> pimpl;
IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING
};
}
}

#endif
148 changes: 148 additions & 0 deletions core/include/gz/plugin/Factory.hh
Original file line number Diff line number Diff line change
@@ -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 <chrono>
#include <functional>
#include <memory>
#include <tuple>

#include <gz/plugin/EnablePluginFromThis.hh>

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 <typename Interface> 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 <typename Interface>
using ProductPtr =
std::unique_ptr<Interface, ProductDeleter<Interface>>;

/// \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<InterfaceClass, InputType1, InputType2>;
/// \endcode
///
/// defines a factory that can produce a `std::unique_ptr<InterfaceClass>`
/// 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 <typename Interface, typename... Args>
class Factory : public EnablePluginFromThis
{
public: using ProductPtrType = ProductPtr<Interface>;

/// \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<Product>
/// 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 <typename Product>
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 <gz/plugin/detail/Factory.hh>

#endif
105 changes: 105 additions & 0 deletions core/include/gz/plugin/Info.hh
Original file line number Diff line number Diff line change
@@ -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 <functional>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>

#include <gz/utilities/SuppressWarning.hh>

#include <gz/plugin/Export.hh>

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<std::string> 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<void*(void*)> >;
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<std::string> 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<void*()> 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<void(void*)> 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<std::string, Info>;
using ConstInfoPtr = std::shared_ptr<const Info>;
}
}

#endif
Loading