plugin_interface.hpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010 by Joel Andersson, Moritz Diehl, K.U.Leuven. All rights reserved.
6  *
7  * CasADi is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 3 of the License, or (at your option) any later version.
11  *
12  * CasADi is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with CasADi; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  */
22 
23 #ifndef CASADI_PLUGIN_INTERFACE_HPP
24 #define CASADI_PLUGIN_INTERFACE_HPP
25 
26 #include "function_internal.hpp"
27 #include "global_options.hpp"
28 #include "serializing_stream.hpp"
29 #include "casadi_os.hpp"
30 #include <casadi/core/casadi_common.hpp>
31 #include "casadi_meta.hpp"
32 
33 #include <stdlib.h>
34 
36 
37 namespace casadi {
38  // Avoid segmentation faults when exposed function not implemented
39  template<typename T>
40  T check_exposed(T t) {
41  casadi_assert(t!=0, "Static function not implemented for plugin");
42  return t;
43  }
44 
45  typedef ProtoFunction* (*Deserialize)(DeserializingStream&);
46 
53  template<class Derived>
54  class PluginInterface {
55  public:
56 
57 
59  struct Plugin{
60  typename Derived::Creator creator;
61  const char* name;
62  const char* doc;
63  int version;
64  typename Derived::Exposed exposed;
65  const Options* options;
66  Deserialize deserialize;
67  // Constructor
68  Plugin() : creator(nullptr), name(nullptr), doc(nullptr), version(0),
69  exposed(), options(nullptr), deserialize(nullptr) {}
70  };
71 
72  // Plugin registration function
73  typedef int (*RegFcn)(Plugin* plugin);
74 
76  static bool has_plugin(const std::string& pname, bool verbose=false);
77 
79  static const Options& plugin_options(const std::string& pname);
80 
82  static Deserialize plugin_deserialize(const std::string& pname);
83 
85  static Plugin pluginFromRegFcn(RegFcn regfcn);
86 
88  static Plugin load_plugin(const std::string& pname,
89  bool register_plugin=true, bool needs_lock=true);
90 
92  static handle_t load_library(const std::string& libname, std::string& resultpath,
93  bool global);
94 
96  static void registerPlugin(const Plugin& plugin, bool needs_lock=true);
97 
99  static void registerPlugin(RegFcn regfcn, bool needs_lock=true);
100 
102  static Plugin& getPlugin(const std::string& pname);
103 
104  // Create solver instance
105  template<class Problem>
106  static Derived* instantiate(const std::string& fname,
107  const std::string& pname, Problem problem);
108  // Get name of the plugin
109  virtual const char* plugin_name() const = 0;
110 
111  // Check if dependencies of the plugin have the correct version
112  virtual void deps_version_check(const std::string& stage) const {}
113 
117  void serialize_type(SerializingStream& s) const {
118  s.pack("PluginInterface::plugin_name", std::string(plugin_name()));
119  }
120 
124  static ProtoFunction* deserialize(DeserializingStream& s) {
125  std::string class_name, plugin_name;
126  s.unpack("PluginInterface::plugin_name", plugin_name);
127  Deserialize deserialize = plugin_deserialize(plugin_name);
128  return deserialize(s);
129  }
130 
131  };
132 
133  template<class Derived>
134  bool PluginInterface<Derived>::has_plugin(const std::string& pname, bool verbose) {
135 
136 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
137  std::lock_guard<std::mutex> lock(Derived::mutex_solvers_);
138 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
139 
140  // Quick return if available
141  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
142  return true;
143  }
144 
145  // Try loading the plugin
146  try {
147  (void)load_plugin(pname, false, false);
148  return true;
149  } catch (CasadiException& ex) {
150  if (verbose) {
151  casadi_warning(ex.what());
152  }
153  return false;
154  }
155  }
156 
157  template<class Derived>
158  const Options& PluginInterface<Derived>::plugin_options(const std::string& pname) {
159  const Options *op = getPlugin(pname).options;
160  casadi_assert(op!=nullptr, "Plugin \"" + pname + "\" does not support options");
161  return *op;
162  }
163 
164  template<class Derived>
165  Deserialize PluginInterface<Derived>::plugin_deserialize(const std::string& pname) {
166  Deserialize m = getPlugin(pname).deserialize;
167  casadi_assert(m, "Plugin \"" + pname + "\" does not support deserialize");
168  return m;
169  }
170 
171  template<class Derived>
172  typename PluginInterface<Derived>::Plugin
173  PluginInterface<Derived>::pluginFromRegFcn(RegFcn regfcn) {
174  // Create a temporary struct
175  Plugin plugin;
176 
177  // Set the fields
178  int flag = regfcn(&plugin);
179  casadi_assert(flag==0, "Registration of plugin failed.");
180 
181  return plugin;
182  }
183 
184 
185  template<class Derived>
186  handle_t PluginInterface<Derived>::load_library(const std::string& libname,
187  std::string& resultpath, bool global) {
188 
189 #ifndef WITH_DL
190  casadi_error("WITH_DL option needed for dynamic loading");
191 #else // WITH_DL
192 
193  // Get the name of the shared library
194  std::string lib = std::string(CasadiMeta::shared_library_prefix()) + libname +
196 
197  // Build up search paths;
198  std::vector<std::string> search_paths = get_search_paths();
199  return open_shared_library(lib, search_paths, resultpath,
200  "PluginInterface::load_plugin", global);
201 
202 #endif // WITH_DL
203  }
204 
205  template<class Derived>
206  typename PluginInterface<Derived>::Plugin
207  PluginInterface<Derived>::load_plugin(const std::string& pname,
208  bool register_plugin, bool needs_lock) {
209 
210 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
211  casadi::conditional_lock_guard<std::mutex> lock(Derived::mutex_solvers_, needs_lock);
212 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
213 
214  // Issue warning and quick return if already loaded
215  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
216  casadi_warning("PluginInterface: Solver " + pname + " is already in use. Ignored.");
217  return Plugin();
218  }
219 
220  // Logger singletons are lazily instantiated on first uout()/uerr() calls
221  // This instantation may lead to a data race with potential instatiations in plugin
222  // To be safe, trigger instantatin before any plugin loading
223  uout();
224  uerr();
225 
226 #ifndef WITH_DL
227  casadi_error("WITH_DL option needed for dynamic loading");
228 #else // WITH_DL
229  // Retrieve the registration function
230  RegFcn reg;
231 
232  // Load the dll
233  std::string regName = "casadi_register_" + Derived::infix_ + "_" + pname;
234 
235  std::string searchpath;
236  handle_t handle = load_library("casadi_" + Derived::infix_ + "_" + pname, searchpath,
237  false);
238 
239 #ifdef _WIN32
240 
241 #if __GNUC__
242 #pragma GCC diagnostic push
243 #pragma GCC diagnostic ignored "-Wcast-function-type"
244 #endif
245  reg = reinterpret_cast<RegFcn>(GetProcAddress(handle, TEXT(regName.c_str())));
246 #if __GNUC__
247 #pragma GCC diagnostic pop
248 #endif
249 
250 #else // _WIN32
251  // Reset error
252  dlerror();
253 
254  // Load creator
255  reg = reinterpret_cast<RegFcn>(dlsym(handle, regName.c_str()));
256 #endif // _WIN32
257  casadi_assert(reg!=nullptr,
258  "PluginInterface::load_plugin: no \"" + regName + "\" found in " + searchpath + ".");
259 
260  // Create a temporary struct
261  Plugin plugin = pluginFromRegFcn(reg);
262  // Register the plugin
263  if (register_plugin) {
264  registerPlugin(plugin, false);
265  }
266 
267  return plugin;
268 
269 #endif // WITH_DL
270  }
271 
272  template<class Derived>
273  void PluginInterface<Derived>::registerPlugin(RegFcn regfcn, bool needs_lock) {
274  registerPlugin(pluginFromRegFcn(regfcn), needs_lock);
275  }
276 
277  template<class Derived>
278  void PluginInterface<Derived>::registerPlugin(const Plugin& plugin, bool needs_lock) {
279 
280 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
281  casadi::conditional_lock_guard<std::mutex> lock(Derived::mutex_solvers_, needs_lock);
282 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
283 
284  // Check if the solver name is in use
285  typename std::map<std::string, Plugin>::iterator it=Derived::solvers_.find(plugin.name);
286  casadi_assert(it==Derived::solvers_.end(),
287  "Solver " + str(plugin.name) + " is already in use");
288 
289  // Add to list of solvers
290  Derived::solvers_[plugin.name] = plugin;
291  }
292 
293  template<class Derived>
294  typename PluginInterface<Derived>::Plugin&
295  PluginInterface<Derived>::getPlugin(const std::string& pname) {
296 
297 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
298  std::lock_guard<std::mutex> lock(Derived::mutex_solvers_);
299 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
300 
301  // Check if the solver has been loaded
302  auto it=Derived::solvers_.find(pname);
303 
304  // Load the solver if needed
305  if (it==Derived::solvers_.end()) {
306  load_plugin(pname, true, false);
307  it=Derived::solvers_.find(pname);
308  }
309  casadi_assert_dev(it!=Derived::solvers_.end());
310  return it->second;
311  }
312 
313  template<class Derived>
314  template<class Problem>
315  Derived* PluginInterface<Derived>::
316  instantiate(const std::string& fname,
317  const std::string& pname, Problem problem) {
318 
319  // Assert the plugin exists (needed for adaptors)
320  if (!has_plugin(pname, true)) {
321  casadi_error("Plugin '" + pname + "' is not found.");
322  }
323  return getPlugin(pname).creator(fname, problem);
324  }
325 
326 } // namespace casadi
327 
329 
330 #endif // CASADI_PLUGIN_INTERFACE_HPP
static const char * shared_library_prefix()
Obtain shared library prefix.
static const char * shared_library_suffix()
Obtain shared library suffix.
The casadi namespace.
Definition: archiver.hpp:32
CASADI_EXPORT std::ostream & uout()
CASADI_EXPORT std::ostream & uerr()