Ignition Common

API Reference

3.0.0
detail/PluginMacros.hh
Go to the documentation of this file.
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_COMMON_DETAIL_PLUGINMACROS_HH_
20 #define IGNITION_COMMON_DETAIL_PLUGINMACROS_HH_
21 
22 #include <string>
23 #include <typeinfo>
24 #include <type_traits>
25 #include <unordered_set>
26 #include <utility>
29 
30 
31 #if defined _WIN32 || defined __CYGWIN__
32  #ifdef __GNUC__
33  #define DETAIL_IGN_PLUGIN_VISIBLE __attribute__ ((dllexport))
34  #else
35  #define DETAIL_IGN_PLUGIN_VISIBLE __declspec(dllexport)
36  #endif
37 #else
38  #if __GNUC__ >= 4
39  #define DETAIL_IGN_PLUGIN_VISIBLE __attribute__ ((visibility ("default")))
40  #else
41  #define DETAIL_IGN_PLUGIN_VISIBLE
42  #endif
43 #endif
44 
45 #define DETAIL_IGN_COMMON_SPECIALIZE_INTERFACE(interfaceName) \
46  static_assert(std::is_same<interfaceName, ::interfaceName>::value, \
47  #interfaceName " must be fully qualified like ::ns::MyClass"); \
48  static constexpr const char* IGNCOMMONInterfaceName = #interfaceName;
49 
51 #define DETAIL_IGN_COMMON_REGISTER_PLUGININFO_META_DATA \
52  extern "C" { \
53  std::size_t DETAIL_IGN_PLUGIN_VISIBLE IGNCOMMONPluginInfoSize = \
54  sizeof(ignition::common::PluginInfo); \
55  \
56  std::size_t DETAIL_IGN_PLUGIN_VISIBLE IGNCOMMONPluginInfoAlignment = \
57  alignof(ignition::common::PluginInfo); \
58  \
59  int DETAIL_IGN_PLUGIN_VISIBLE IGNCOMMONPluginAPIVersion = \
60  ignition::common::PLUGIN_API_VERSION; \
61  }
62 
63 
64 #define DETAIL_IGN_COMMON_BEGIN_ADDING_PLUGINS \
65  DETAIL_IGN_COMMON_REGISTER_PLUGININFO_META_DATA \
66 IGN_COMMON_WARN_IGNORE__DELETE_NON_VIRTUAL_DESTRUCTOR \
67  /* This struct attempts to make sure that the macro is being called from */ \
68  /* a global namespace */ \
69  struct IGN_macro_must_be_used_in_global_namespace; \
70  static_assert(std::is_same < IGN_macro_must_be_used_in_global_namespace, \
71  ::IGN_macro_must_be_used_in_global_namespace>::value, \
72  "Macro for registering plugins must be used in global namespace"); \
73  \
74  /* \brief Extract PluginInfo from this dynamically loaded library */ \
75  \
76  /* \param[out] _outputInfo A void* which will be filled with the */ \
77  /* PluginInfo that corresponds to _pluginId */ \
78  \
79  /* \param[in] _pluginId The index of the plugin which we want to load */ \
80  /* during this call to the function */ \
81  \
82  /* \param[in] _size The expected size of a single PluginInfo object, to */ \
83  /* ensure that this function agrees with the PluginLoader about the size */ \
84  /* of a PluginInfo object. */ \
85  \
86  /* \return The number of plugins from _pluginId onward that are remaining */ \
87  /* in this library. The expression */ \
88  /* (IGNCOMMONMultiPluginInfo(info, id, size) > 0) will evaluate as true */ \
89  /* if `info` has been filled with useful plugin information. */ \
90  extern "C" std::size_t DETAIL_IGN_PLUGIN_VISIBLE IGNCOMMONMultiPluginInfo( \
91  void * * const _outputInfo, \
92  const std::size_t _pluginId, \
93  const std::size_t _size) \
94  { \
95  if (_size != sizeof(ignition::common::PluginInfo)) \
96  { \
97  return 0u; \
98  } \
99  std::unordered_set<std::string> visitedPlugins; \
100  ignition::common::PluginInfo * * const ptrToPlugin = \
101  reinterpret_cast<ignition::common::PluginInfo * *>(_outputInfo); \
102  if ( !(*ptrToPlugin) ) \
103  { \
104  *ptrToPlugin = new ignition::common::PluginInfo; \
105  } \
106  ignition::common::PluginInfo *plugin = *ptrToPlugin; \
107  plugin->name.clear(); \
108  plugin->interfaces.clear(); \
109  plugin->factory = nullptr; \
110  plugin->deleter = nullptr;
111 
112 
113 #define DETAIL_IGN_COMMON_ADD_PLUGIN(pluginName, interface) \
114  /* Attempt to ensure that the user provides fully-qualified class names*/ \
115  static_assert(std::is_same<pluginName, ::pluginName>::value, \
116  #pluginName " must be fully qualified like ::ns::MyClass"); \
117  static_assert(std::is_same<interface, ::interface>::value, \
118  #interface " must be fully qualified like ::ns::MyClass"); \
119  \
120  /* Print out a clear error when the plugin class is pure abstract (which */ \
121  /* would make it impossible to load as a plugin). The compiler prevents */ \
122  /* this from being an issue, but its printout might be difficult for */ \
123  /* users to interpret, so we provide a very explicit explanation here. */ \
124  static_assert(!std::is_abstract<pluginName>::value, \
125  "[" #pluginName "] must not be an abstract class. It contains at least " \
126  "one pure virtual function!"); \
127  /* Print out a clear error when the plugin does not actually provide the */ \
128  /* specified interface. */ \
129  static_assert(std::is_base_of<interface, pluginName>::value, \
130  "[" #interface "] is not a base class of [" #pluginName "], so it " \
131  "cannot be used as a plugin interface for [" #pluginName "]!"); \
132  /* We create an extra scope here so that the same variable names can be */ \
133  /* reused between calls to this macro without any risk of interfering */ \
134  /* with each other. */\
135  { \
136  const bool insertion = visitedPlugins.insert(#pluginName).second; \
137  if (insertion) /* NOLINT(*) */ \
138  { \
139  if (_pluginId == visitedPlugins.size() - 1) /* NOLINT(*) */ \
140  { \
141  /* If the visitedPlugins has reached the requested _pluginId, fill */ \
142  /* in the PluginInfo output parameter. */ \
143  plugin->name = #pluginName; \
144  plugin->factory = []() { \
145  return static_cast<void*>(new pluginName()); \
146  }; \
147  plugin->deleter = [](void* ptr) { \
148  delete static_cast< pluginName* >(ptr); \
149  }; \
150  } \
151  } \
152  \
153  if ( #pluginName == plugin->name ) /* NOLINT(*) */ \
154  { \
155  /* If the name of the desired plugin matches this call to the macro, */ \
156  /* add a map entry for the interface specified by this macro. */ \
157  plugin->interfaces.insert(std::make_pair( \
158  #interface , [=](void* v_ptr) { \
159  pluginName * d_ptr = static_cast< pluginName *>(v_ptr); \
160  return static_cast< interface *>(d_ptr); \
161  })); \
162  } \
163  }
164 
165 
166 #define DETAIL_IGN_COMMON_FINISH_ADDING_PLUGINS \
167  if (_pluginId >= visitedPlugins.size()) /* NOLINT(*) */ \
168  { \
169  if (plugin) \
170  delete plugin; \
171  return 0u; \
172  } \
173  return visitedPlugins.size() - _pluginId; \
174  } \
175 IGN_COMMON_WARN_RESUME__DELETE_NON_VIRTUAL_DESTRUCTOR
176 
177 
178 #endif