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 #include "ignition/common/Plugin.hh" 20 #include "ignition/common/PluginInfo.hh" 21 #include "ignition/common/Console.hh" 22 #include "PluginUtils.hh" 23 24 namespace ignition 25 { 26 namespace common 27 { 28 class PluginPrivate 29 { 30 /// \brief Clear this PluginPrivate without invaliding any map entry 31 /// iterators. Clear()32 public: void Clear() 33 { 34 this->loadedInstancePtr.reset(); 35 36 // Dev note (MXG): We must NOT call clear() on the InterfaceMap or 37 // remove ANY of the map entries, because that would potentially 38 // invalidate all of the iterators that are pointing to map entries. 39 // This would break any specialized plugins that provide instant access 40 // to specialized interfaces. Instead, we simply overwrite the map 41 // entries with a nullptr. 42 for (auto &entry : this->interfaces) 43 entry.second = nullptr; 44 } 45 46 /// \brief Initialize this PluginPrivate using some PluginInfo instance 47 /// \param[in] _info Information describing the plugin to initialize Initialize(const PluginInfo * _info)48 public: void Initialize(const PluginInfo *_info) 49 { 50 this->Clear(); 51 52 if (!_info) 53 return; 54 55 this->loadedInstancePtr = 56 std::shared_ptr<void>(_info->factory(), _info->deleter); 57 58 if (this->loadedInstancePtr) 59 { 60 for (const auto &entry : _info->interfaces) 61 { 62 // entry.first: name of the interface 63 // entry.second: function which casts the pluginInstance pointer to 64 // the correct location of the interface within the 65 // plugin 66 this->interfaces[entry.first] = 67 entry.second(this->loadedInstancePtr.get()); 68 } 69 } 70 } 71 72 /// \brief Initialize this PluginPrivate using another instance 73 /// \param[in] _other Another instance of a PluginPrivate object Initialize(const PluginPrivate * _other)74 public: void Initialize(const PluginPrivate *_other) 75 { 76 this->Clear(); 77 78 if (!_other) 79 { 80 ignerr << "Received a nullptr _other in the constructor " 81 << "which uses `const PluginPrivate*`. This should " 82 << "not be possible! Please report this bug." 83 << std::endl; 84 assert(false); 85 return; 86 } 87 88 this->loadedInstancePtr = _other->loadedInstancePtr; 89 90 if (this->loadedInstancePtr) 91 { 92 for (const auto &entry : _other->interfaces) 93 { 94 // entry.first: name of the interface 95 // entry.second: pointer to the location of that interface within 96 // the plugin instance 97 this->interfaces[entry.first] = entry.second; 98 } 99 } 100 } 101 102 /// \brief Map from interface names to their locations within the plugin 103 /// instance 104 // 105 // Dev Note (MXG): We use std::map here instead of std::unordered_map 106 // because iterators to a std::map are not invalidated by the insertion 107 // operation (whereas all iterators to a std::unordered_map are 108 // potentially invalidated each time an insertion is performed on the 109 // std::unordered_map). Holding onto valid iterators allows us to do 110 // optimizations with template magic to provide direct access to 111 // interfaces whose availability we can anticipate at run time. 112 // 113 // It is also worth noting that ordered vs unordered lookup time is very 114 // similar on sets where the strings have relatively small lengths (5-20 115 // characters) and a relatively small number of entries in the set (5-20 116 // entries). Those conditions match our expected use case here. In fact, 117 // ordered lookup can sometimes outperform unordered in these conditions. 118 public: Plugin::InterfaceMap interfaces; 119 120 /// \brief shared_ptr which manages the lifecycle of the plugin instance. 121 std::shared_ptr<void> loadedInstancePtr; 122 }; 123 124 ////////////////////////////////////////////////// HasInterface(const std::string & _interfaceName) const125 bool Plugin::HasInterface( 126 const std::string &_interfaceName) const 127 { 128 const std::string interfaceName = NormalizeName(_interfaceName); 129 return (this->dataPtr->interfaces.count(interfaceName) != 0); 130 } 131 132 ////////////////////////////////////////////////// Plugin()133 Plugin::Plugin() 134 : dataPtr(new PluginPrivate) 135 { 136 // Do nothing 137 } 138 139 ////////////////////////////////////////////////// PrivateGetInterface(const std::string & _interfaceName) const140 void *Plugin::PrivateGetInterface( 141 const std::string &_interfaceName) const 142 { 143 const std::string interfaceName = NormalizeName(_interfaceName); 144 const auto &it = this->dataPtr->interfaces.find(interfaceName); 145 if (this->dataPtr->interfaces.end() == it) 146 return nullptr; 147 148 return it->second; 149 } 150 151 ////////////////////////////////////////////////// PrivateCopyPluginInstance(const Plugin & _other) const152 void Plugin::PrivateCopyPluginInstance(const Plugin &_other) const 153 { 154 this->dataPtr->Initialize(_other.dataPtr.get()); 155 } 156 157 ////////////////////////////////////////////////// PrivateSetPluginInstance(const PluginInfo * _info) const158 void Plugin::PrivateSetPluginInstance(const PluginInfo *_info) const 159 { 160 this->dataPtr->Initialize(_info); 161 } 162 163 ////////////////////////////////////////////////// PrivateGetInstancePtr() const164 const std::shared_ptr<void> &Plugin::PrivateGetInstancePtr() const 165 { 166 return this->dataPtr->loadedInstancePtr; 167 } 168 169 ////////////////////////////////////////////////// PrivateGetOrCreateIterator(const std::string & _interfaceName)170 Plugin::InterfaceMap::iterator Plugin::PrivateGetOrCreateIterator( 171 const std::string &_interfaceName) 172 { 173 // We want to use the insert function here to avoid accidentally 174 // overwriting a value which might exist at the desired map key. 175 return this->dataPtr->interfaces.insert( 176 std::make_pair(NormalizeName(_interfaceName), nullptr)).first; 177 } 178 179 ////////////////////////////////////////////////// ~Plugin()180 Plugin::~Plugin() 181 { 182 // Do nothing. We need this definition to be in a source file so that 183 // the destructor of PluginPrivate is visible to std::unique_ptr. 184 } 185 } 186 } 187