1 /*
2  * Copyright (C) 2017 Open Source Robotics Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 
19 #ifndef IGNITION_PLUGIN_PLUGIN_HH_
20 #define IGNITION_PLUGIN_PLUGIN_HH_
21 
22 #include <memory>
23 #include <map>
24 #include <string>
25 
26 #include <ignition/utilities/SuppressWarning.hh>
27 
28 #include <ignition/plugin/Export.hh>
29 #include <ignition/plugin/Info.hh>
30 
31 namespace ignition
32 {
33   namespace plugin
34   {
35     // Forward declaration
36     namespace detail { template <class, class> class ComposePlugin; }
37     class EnablePluginFromThis;
38     class WeakPluginPtr;
39 
40     class IGNITION_PLUGIN_VISIBLE Plugin
41     {
42       // -------------------- Public API ---------------------
43 
44       /// \brief Get an interface of the specified type, if it is provided by
45       /// this plugin.
46       ///
47       /// Note that the interface pointer you receive is owned by the Plugin
48       /// object. You MUST NOT ever try to deallocate it yourself. Moreover, the
49       /// pointer will be invalidated once all Plugin objects that refer to the
50       /// same Plugin instance are destructed. Use the QueryInterfaceSharedPtr
51       /// function in order to get a reference-counting pointer to an interface
52       /// of this Plugin object. The pointer will remain valid as long as the
53       /// std::shared_ptr provided by QueryInterfaceSharedPtr is alive.
54       ///
55       /// \return A raw pointer to the specified interface. If the requested
56       /// _interfaceName is not provided by this Plugin, this returns a nullptr.
57       /// This pointer is invalidated when the reference count of the plugin
58       /// instance drops to zero.
59       public: template <class Interface>
60               Interface *QueryInterface();
61 
62       /// \brief const-qualified version of QueryInterface<Interface>()
63       public: template <class Interface>
64               const Interface *QueryInterface() const;
65 
66       /// \brief This function has been deprecated in favor of the version of
67       /// QueryInterface which does not take a std::string argument.
68       public: template <class Interface>
69               IGN_DEPRECATED(0.0)
70               Interface *QueryInterface(const std::string &/*_interfaceName*/);
71 
72       /// \brief const-qualified version of
73       /// QueryInterface<Interface>(std::string)
74       public: template <class Interface>
75               IGN_DEPRECATED(0.0)
76               const Interface *QueryInterface(
77                   const std::string &/*_interfaceName*/) const;
78 
79       /// \brief Get the requested interface as a std::shared_ptr. The template
80       /// argument Interface must exactly match the underlying type associated
81       /// with _interfaceName, or else the behavior of this function is
82       /// undefined.
83       ///
84       /// This std::shared_ptr and the interface+plugin that it refers to will
85       /// remain valid, even if all Plugin objects which refer to the plugin
86       /// instance are destructed.
87       ///
88       /// You MUST NOT attempt to pass a QueryInterface pointer into a
89       /// std::shared_ptr yourself; that will result in double-delete memory
90       /// errors. You must always call QueryInterfaceSharedPtr for a reference-
91       /// counting pointer to an interface.
92       ///
93       /// \param[in] _interfaceName The name of the desired interface, as a
94       /// string.
95       /// \return A reference-counting pointer to the specified interface. This
96       /// will keep the interface valid and the plugin instance alive, even if
97       /// all Plugin objects that refer to this plugin instance are destructed.
98       public: template <class Interface>
99               std::shared_ptr<Interface> QueryInterfaceSharedPtr();
100 
101       /// \brief Same as QueryInterfaceSharedPtr<Interface>(), but it returns a
102       /// std::shared_ptr to a const-qualified Interface.
103       public: template <class Interface>
104               std::shared_ptr<const Interface> QueryInterfaceSharedPtr() const;
105 
106       /// \brief This version of QueryInterfaceSharedPtr has been deprecated in
107       /// favor of the version that does not take a std::string argument.
108       public: template <class Interface>
109               IGN_DEPRECATED(0.0)
110               std::shared_ptr<Interface> QueryInterfaceSharedPtr(
111                   const std::string &/*_interfaceName*/);
112 
113       /// \brief Same as QueryInterfaceSharedPtr<Interface>(std::string), but
114       /// it returns a std::shared_ptr to a const-qualified Interface.
115       public: template <class Interface>
116               IGN_DEPRECATED(0.0)
117               std::shared_ptr<const Interface> QueryInterfaceSharedPtr(
118                   const std::string &/*_interfaceName*/) const;
119 
120       /// \brief Returns true if this Plugin has the specified type of
121       /// interface.
122       public: template <class Interface>
123               bool HasInterface() const;
124 
125       /// \brief Returns true if this Plugin has the specified type of
126       /// interface, otherwise returns false.
127       ///
128       /// By default, we expect you to pass in a demangled version of the
129       /// interface name. If you want to use a mangled version of the name,
130       /// set the `demangled` argument to false.
131       ///
132       /// \param[in] _interfaceName The name of the desired interface, as a
133       /// std::string. Note that this expects the name to be mangled.
134       /// \param[in] _demangled If _interfaceName is demangled, set this to
135       /// true. If you are instead using the raw mangled name that gets provided
136       /// by typeid(T).name(), then set _demangled to false.
137       public: bool HasInterface(const std::string &_interfaceName,
138                                 const bool _demangled = true) const;
139 
140 
141       // -------------------- Private API -----------------------
142 
143       template <class> friend class TemplatePluginPtr;
144       template <class...> friend class SpecializedPlugin;
145       template <class, class> friend class detail::ComposePlugin;
146       friend class EnablePluginFromThis;
147       friend class WeakPluginPtr;
148 
149       /// \brief Default constructor. This is kept private to ensure that
150       /// Plugins are always managed by a PluginPtr object.
151       private: Plugin();
152 
153       /// \brief Type-agnostic retriever for interfaces
154       private: void *PrivateQueryInterface(
155                   const std::string &_interfaceName) const;
156 
157       /// \brief Copy the plugin instance from another Plugin object
158       private: void PrivateCopyPluginInstance(const Plugin &_other) const;
159 
160       /// \brief Copy an existing plugin instance into this plugin
161       /// \param[in] _info
162       ///   Pointer to the Info for this plugin
163       /// \param[in] _instancePtr
164       ///   Pointer to an already-existing abstract plugin instance pointer
165       private: void PrivateCopyPluginInstance(
166                   const ConstInfoPtr &_info,
167                   const std::shared_ptr<void> &_instancePtr) const;
168 
169       /// \brief Create a new plugin instance based on the info provided
170       /// \param[in] _info
171       ///   Pointer to the Info for this plugin
172       /// \param[in] _dlHandlePtr
173       ///   Reference counter for the dl handle of this Plugin
174       private: void PrivateCreatePluginInstance(
175                   const ConstInfoPtr &_info,
176                   const std::shared_ptr<void> &_dlHandlePtr) const;
177 
178       /// \brief Get a reference to the abstract instance being managed by this
179       /// wrapper
180       private: const std::shared_ptr<void> &PrivateGetInstancePtr() const;
181 
182       /// \brief Get a reference to the Info being used by this wrapper
183       private: const ConstInfoPtr &PrivateGetInfoPtr() const;
184 
185       /// \brief The InterfaceMap type needs to get used in several places, like
186       /// Plugin::Implementation and SpecializedPlugin<T>. We make the typedef
187       /// public so that those other classes can use it without needing to be
188       /// friends of Plugin. End-users should not have any need for this
189       /// typedef.
190       public: using InterfaceMap = std::map<std::string, void*>;
191 
192       /// \brief Get or create an iterator to the std::map that holds pointers
193       /// to the various interfaces provided by this plugin instance.
194       private: InterfaceMap::iterator PrivateGetOrCreateIterator(
195           const std::string &_interfaceName);
196 
197       class Implementation;
198       IGN_UTILS_WARN_IGNORE__DLL_INTERFACE_MISSING
199       /// \brief PIMPL pointer to the implementation of this class.
200       private: const std::unique_ptr<Implementation> dataPtr;
201       IGN_UTILS_WARN_RESUME__DLL_INTERFACE_MISSING
202 
203       /// \brief Virtual destructor
204       public: virtual ~Plugin();
205     };
206   }
207 }
208 
209 #include "ignition/plugin/detail/Plugin.hh"
210 
211 #endif
212