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/config.h>
31 #include <casadi/core/casadi_common.hpp>
32 
33 #include <stdlib.h>
34 
36 
37 namespace casadi {
38  // Avoid segmentation faults when exposed function not implemented
39  template<typename 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>
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;
67  // Constructor
68  Plugin() : creator(nullptr), name(nullptr), doc(nullptr), version(0),
69  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 
115  s.pack("PluginInterface::plugin_name", std::string(plugin_name()));
116  }
117 
122  std::string class_name, plugin_name;
123  s.unpack("PluginInterface::plugin_name", plugin_name);
125  return deserialize(s);
126  }
127 
128  };
129 
130  template<class Derived>
131  bool PluginInterface<Derived>::has_plugin(const std::string& pname, bool verbose) {
132 
133 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
134  std::lock_guard<std::mutex> lock(Derived::mutex_solvers_);
135 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
136 
137  // Quick return if available
138  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
139  return true;
140  }
141 
142  // Try loading the plugin
143  try {
144  (void)load_plugin(pname, false, false);
145  return true;
146  } catch (CasadiException& ex) {
147  if (verbose) {
148  casadi_warning(ex.what());
149  }
150  return false;
151  }
152  }
153 
154  template<class Derived>
155  const Options& PluginInterface<Derived>::plugin_options(const std::string& pname) {
156  const Options *op = getPlugin(pname).options;
157  casadi_assert(op!=nullptr, "Plugin \"" + pname + "\" does not support options");
158  return *op;
159  }
160 
161  template<class Derived>
163  Deserialize m = getPlugin(pname).deserialize;
164  casadi_assert(m, "Plugin \"" + pname + "\" does not support deserialize");
165  return m;
166  }
167 
168  template<class Derived>
171  // Create a temporary struct
172  Plugin plugin;
173 
174  // Set the fields
175  int flag = regfcn(&plugin);
176  casadi_assert(flag==0, "Registration of plugin failed.");
177 
178  return plugin;
179  }
180 
181 
182  template<class Derived>
183  handle_t PluginInterface<Derived>::load_library(const std::string& libname,
184  std::string& resultpath, bool global) {
185 
186 #ifndef WITH_DL
187  casadi_error("WITH_DL option needed for dynamic loading");
188 #else // WITH_DL
189 
190  // Get the name of the shared library
191  std::string lib = SHARED_LIBRARY_PREFIX + libname + SHARED_LIBRARY_SUFFIX;
192 
193  // Build up search paths;
194  std::vector<std::string> search_paths = get_search_paths();
195  return open_shared_library(lib, search_paths, resultpath,
196  "PluginInterface::load_plugin", global);
197 
198 #endif // WITH_DL
199  }
200 
201  template<class Derived>
203  PluginInterface<Derived>::load_plugin(const std::string& pname,
204  bool register_plugin, bool needs_lock) {
205 
206 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
207  casadi::conditional_lock_guard<std::mutex> lock(Derived::mutex_solvers_, needs_lock);
208 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
209 
210  // Issue warning and quick return if already loaded
211  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
212  casadi_warning("PluginInterface: Solver " + pname + " is already in use. Ignored.");
213  return Plugin();
214  }
215 
216  // Logger singletons are lazily instantiated on first uout()/uerr() calls
217  // This instantation may lead to a data race with potential instatiations in plugin
218  // To be safe, trigger instantatin before any plugin loading
219  uout();
220  uerr();
221 
222 #ifndef WITH_DL
223  casadi_error("WITH_DL option needed for dynamic loading");
224 #else // WITH_DL
225  // Retrieve the registration function
226  RegFcn reg;
227 
228  // Load the dll
229  std::string regName = "casadi_register_" + Derived::infix_ + "_" + pname;
230 
231  std::string searchpath;
232  handle_t handle = load_library("casadi_" + Derived::infix_ + "_" + pname, searchpath,
233  false);
234 
235 #ifdef _WIN32
236 
237 #if __GNUC__
238 #pragma GCC diagnostic push
239 #pragma GCC diagnostic ignored "-Wcast-function-type"
240 #endif
241  reg = reinterpret_cast<RegFcn>(GetProcAddress(handle, TEXT(regName.c_str())));
242 #if __GNUC__
243 #pragma GCC diagnostic pop
244 #endif
245 
246 #else // _WIN32
247  // Reset error
248  dlerror();
249 
250  // Load creator
251  reg = reinterpret_cast<RegFcn>(dlsym(handle, regName.c_str()));
252 #endif // _WIN32
253  casadi_assert(reg!=nullptr,
254  "PluginInterface::load_plugin: no \"" + regName + "\" found in " + searchpath + ".");
255 
256  // Create a temporary struct
257  Plugin plugin = pluginFromRegFcn(reg);
258  // Register the plugin
259  if (register_plugin) {
260  registerPlugin(plugin, false);
261  }
262 
263  return plugin;
264 
265 #endif // WITH_DL
266  }
267 
268  template<class Derived>
269  void PluginInterface<Derived>::registerPlugin(RegFcn regfcn, bool needs_lock) {
270  registerPlugin(pluginFromRegFcn(regfcn), needs_lock);
271  }
272 
273  template<class Derived>
274  void PluginInterface<Derived>::registerPlugin(const Plugin& plugin, bool needs_lock) {
275 
276 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
277  casadi::conditional_lock_guard<std::mutex> lock(Derived::mutex_solvers_, needs_lock);
278 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
279 
280  // Check if the solver name is in use
281  typename std::map<std::string, Plugin>::iterator it=Derived::solvers_.find(plugin.name);
282  casadi_assert(it==Derived::solvers_.end(),
283  "Solver " + str(plugin.name) + " is already in use");
284 
285  // Add to list of solvers
286  Derived::solvers_[plugin.name] = plugin;
287  }
288 
289  template<class Derived>
291  PluginInterface<Derived>::getPlugin(const std::string& pname) {
292 
293 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
294  std::lock_guard<std::mutex> lock(Derived::mutex_solvers_);
295 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
296 
297  // Check if the solver has been loaded
298  auto it=Derived::solvers_.find(pname);
299 
300  // Load the solver if needed
301  if (it==Derived::solvers_.end()) {
302  load_plugin(pname, true, false);
303  it=Derived::solvers_.find(pname);
304  }
305  casadi_assert_dev(it!=Derived::solvers_.end());
306  return it->second;
307  }
308 
309  template<class Derived>
310  template<class Problem>
312  instantiate(const std::string& fname,
313  const std::string& pname, Problem problem) {
314 
315  // Assert the plugin exists (needed for adaptors)
316  if (!has_plugin(pname, true)) {
317  casadi_error("Plugin '" + pname + "' is not found.");
318  }
319  return getPlugin(pname).creator(fname, problem);
320  }
321 
322 } // namespace casadi
323 
325 
326 #endif // CASADI_PLUGIN_INTERFACE_HPP
Casadi exception class.
Definition: exception.hpp:77
const char * what() const override
Display error.
Definition: exception.hpp:90
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
Interface for accessing input and output data structures.
static bool has_plugin(const std::string &pname, bool verbose=false)
Check if a plugin is available or can be loaded.
static handle_t load_library(const std::string &libname, std::string &resultpath, bool global)
Load a library dynamically.
static Derived * instantiate(const std::string &fname, const std::string &pname, Problem problem)
void serialize_type(SerializingStream &s) const
Serialize type information.
int(* RegFcn)(Plugin *plugin)
static Plugin pluginFromRegFcn(RegFcn regfcn)
Instantiate a Plugin struct from a factory function.
static const Options & plugin_options(const std::string &pname)
Get the plugin options.
static Plugin & getPlugin(const std::string &pname)
Load and get the creator function.
static Deserialize plugin_deserialize(const std::string &pname)
Get the plugin deserialize_map.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
static void registerPlugin(RegFcn regfcn, bool needs_lock=true)
Register an integrator in the factory.
virtual const char * plugin_name() const =0
static Plugin load_plugin(const std::string &pname, bool register_plugin=true, bool needs_lock=true)
Load a plugin dynamically.
Base class for FunctionInternal and LinsolInternal.
Helper class for Serialization.
void pack(const Sparsity &e)
Serializes an object to the output stream.
The casadi namespace.
Definition: archiver.cpp:28
ProtoFunction *(* Deserialize)(DeserializingStream &)
std::ostream & uerr()
T check_exposed(T t)
std::vector< casadi_int > find(const std::vector< T > &v)
find nonzeros
std::vector< std::string > get_search_paths()
Definition: casadi_os.cpp:49
std::string str(const T &v)
String representation, any type.
std::ostream & uout()
Options metadata for a class.
Definition: options.hpp:40