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 
32 #include <stdlib.h>
33 
35 
36 namespace casadi {
37  // Avoid segmentation faults when exposed function not implemented
38  template<typename T>
39  T check_exposed(T t) {
40  casadi_assert(t!=0, "Static function not implemented for plugin");
41  return t;
42  }
43 
44  typedef ProtoFunction* (*Deserialize)(DeserializingStream&);
45 
52  template<class Derived>
53  class PluginInterface {
54  public:
55 
56 
58  struct Plugin{
59  typename Derived::Creator creator;
60  const char* name;
61  const char* doc;
62  int version;
63  typename Derived::Exposed exposed;
64  const Options* options;
65  Deserialize deserialize;
66  // Constructor
67  Plugin() : creator(nullptr), name(nullptr), doc(nullptr), version(0),
68  options(nullptr), deserialize(nullptr) {}
69  };
70 
71  // Plugin registration function
72  typedef int (*RegFcn)(Plugin* plugin);
73 
75  static bool has_plugin(const std::string& pname, bool verbose=false);
76 
78  static const Options& plugin_options(const std::string& pname);
79 
81  static Deserialize plugin_deserialize(const std::string& pname);
82 
84  static Plugin pluginFromRegFcn(RegFcn regfcn);
85 
87  static Plugin load_plugin(const std::string& pname, bool register_plugin=true);
88 
90  static handle_t load_library(const std::string& libname, std::string& resultpath,
91  bool global);
92 
94  static void registerPlugin(const Plugin& plugin);
95 
97  static void registerPlugin(RegFcn regfcn);
98 
100  static Plugin& getPlugin(const std::string& pname);
101 
102  // Create solver instance
103  template<class Problem>
104  static Derived* instantiate(const std::string& fname,
105  const std::string& pname, Problem problem);
106  // Get name of the plugin
107  virtual const char* plugin_name() const = 0;
108 
112  void serialize_type(SerializingStream& s) const {
113  s.pack("PluginInterface::plugin_name", std::string(plugin_name()));
114  }
115 
119  static ProtoFunction* deserialize(DeserializingStream& s) {
120  std::string class_name, plugin_name;
121  s.unpack("PluginInterface::plugin_name", plugin_name);
122  Deserialize deserialize = plugin_deserialize(plugin_name);
123  return deserialize(s);
124  }
125 
126  };
127 
128  template<class Derived>
129  bool PluginInterface<Derived>::has_plugin(const std::string& pname, bool verbose) {
130 
131  // Quick return if available
132  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
133  return true;
134  }
135 
136  // Try loading the plugin
137  try {
138  (void)load_plugin(pname, false);
139  return true;
140  } catch (CasadiException& ex) {
141  if (verbose) {
142  casadi_warning(ex.what());
143  }
144  return false;
145  }
146  }
147 
148  template<class Derived>
149  const Options& PluginInterface<Derived>::plugin_options(const std::string& pname) {
150  const Options *op = getPlugin(pname).options;
151  casadi_assert(op!=nullptr, "Plugin \"" + pname + "\" does not support options");
152  return *op;
153  }
154 
155  template<class Derived>
156  Deserialize PluginInterface<Derived>::plugin_deserialize(const std::string& pname) {
157  Deserialize m = getPlugin(pname).deserialize;
158  casadi_assert(m, "Plugin \"" + pname + "\" does not support deserialize");
159  return m;
160  }
161 
162  template<class Derived>
163  typename PluginInterface<Derived>::Plugin
164  PluginInterface<Derived>::pluginFromRegFcn(RegFcn regfcn) {
165  // Create a temporary struct
166  Plugin plugin;
167 
168  // Set the fields
169  int flag = regfcn(&plugin);
170  casadi_assert(flag==0, "Registration of plugin failed.");
171 
172  return plugin;
173  }
174 
175 
176  template<class Derived>
177  handle_t PluginInterface<Derived>::load_library(const std::string& libname,
178  std::string& resultpath, bool global) {
179 
180 #ifndef WITH_DL
181  casadi_error("WITH_DL option needed for dynamic loading");
182 #else // WITH_DL
183 
184  // Get the name of the shared library
185  std::string lib = SHARED_LIBRARY_PREFIX + libname + SHARED_LIBRARY_SUFFIX;
186 
187  // Build up search paths;
188  std::vector<std::string> search_paths = get_search_paths();
189  return open_shared_library(lib, search_paths, resultpath,
190  "PluginInterface::load_plugin", global);
191 
192 #endif // WITH_DL
193  }
194 
195  template<class Derived>
196  typename PluginInterface<Derived>::Plugin
197  PluginInterface<Derived>::load_plugin(const std::string& pname, bool register_plugin) {
198  // Issue warning and quick return if already loaded
199  if (Derived::solvers_.find(pname) != Derived::solvers_.end()) {
200  casadi_warning("PluginInterface: Solver " + pname + " is already in use. Ignored.");
201  return Plugin();
202  }
203 
204 
205 #ifndef WITH_DL
206  casadi_error("WITH_DL option needed for dynamic loading");
207 #else // WITH_DL
208  // Retrieve the registration function
209  RegFcn reg;
210 
211  // Load the dll
212  std::string regName = "casadi_register_" + Derived::infix_ + "_" + pname;
213 
214  std::string searchpath;
215  handle_t handle = load_library("casadi_" + Derived::infix_ + "_" + pname, searchpath,
216  false);
217 
218 #ifdef _WIN32
219 
220 #if __GNUC__
221 #pragma GCC diagnostic push
222 #pragma GCC diagnostic ignored "-Wcast-function-type"
223 #endif
224  reg = reinterpret_cast<RegFcn>(GetProcAddress(handle, TEXT(regName.c_str())));
225 #if __GNUC__
226 #pragma GCC diagnostic pop
227 #endif
228 
229 #else // _WIN32
230  // Reset error
231  dlerror();
232 
233  // Load creator
234  reg = reinterpret_cast<RegFcn>(dlsym(handle, regName.c_str()));
235 #endif // _WIN32
236  casadi_assert(reg!=nullptr,
237  "PluginInterface::load_plugin: no \"" + regName + "\" found in " + searchpath + ".");
238 
239  // Create a temporary struct
240  Plugin plugin = pluginFromRegFcn(reg);
241  // Register the plugin
242  if (register_plugin) {
243  registerPlugin(plugin);
244  }
245 
246  return plugin;
247 
248 #endif // WITH_DL
249  }
250 
251  template<class Derived>
252  void PluginInterface<Derived>::registerPlugin(RegFcn regfcn) {
253  registerPlugin(pluginFromRegFcn(regfcn));
254  }
255 
256  template<class Derived>
257  void PluginInterface<Derived>::registerPlugin(const Plugin& plugin) {
258 
259  // Check if the solver name is in use
260  typename std::map<std::string, Plugin>::iterator it=Derived::solvers_.find(plugin.name);
261  casadi_assert(it==Derived::solvers_.end(),
262  "Solver " + str(plugin.name) + " is already in use");
263 
264  // Add to list of solvers
265  Derived::solvers_[plugin.name] = plugin;
266  }
267 
268  template<class Derived>
269  typename PluginInterface<Derived>::Plugin&
270  PluginInterface<Derived>::getPlugin(const std::string& pname) {
271 
272  // Check if the solver has been loaded
273  auto it=Derived::solvers_.find(pname);
274 
275  // Load the solver if needed
276  if (it==Derived::solvers_.end()) {
277  load_plugin(pname);
278  it=Derived::solvers_.find(pname);
279  }
280  casadi_assert_dev(it!=Derived::solvers_.end());
281  return it->second;
282  }
283 
284  template<class Derived>
285  template<class Problem>
286  Derived* PluginInterface<Derived>::
287  instantiate(const std::string& fname,
288  const std::string& pname, Problem problem) {
289 
290  // Assert the plugin exists (needed for adaptors)
291  if (!has_plugin(pname, true)) {
292  casadi_error("Plugin '" + pname + "' is not found.");
293  }
294  return getPlugin(pname).creator(fname, problem);
295  }
296 
297 } // namespace casadi
298 
300 
301 #endif // CASADI_PLUGIN_INTERFACE_HPP
The casadi namespace.