importer_internal.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "importer_internal.hpp"
27 #include "filesystem_impl.hpp"
28 
29 namespace casadi {
30 
31  ImporterInternal::ImporterInternal(const std::string& name) : name_(name) {
32  verbose_ = false;
33  }
34 
36  }
37 
38  void ImporterInternal::disp(std::ostream &stream, bool more) const {
39  }
40 
41  std::map<std::string, ImporterInternal::Plugin> ImporterInternal::solvers_;
42 
43 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
44  std::mutex ImporterInternal::mutex_solvers_;
45 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
46 
47  const std::string ImporterInternal::infix_ = "importer";
48 
50  = {{},
51  {{"verbose",
52  {OT_BOOL,
53  "Verbose evaluation -- for debugging"}}
54  }
55  };
56 
57  void ImporterInternal::construct(const Dict& opts) {
58  // Sanitize dictionary is needed
59  if (!Options::is_sane(opts)) {
60  // Call recursively
61  return construct(Options::sanitize(opts));
62  }
63 
64  // Make sure all options exist
65  get_options().check(opts);
66 
67  // Initialize object
68  init(opts);
69 
70  // Revisit class hierarchy in reverse order
71  finalize();
72  }
73 
74  void ImporterInternal::init(const Dict& opts) {
75  // Read meta information from file
76  if (can_have_meta()) {
77  casadi_int offset = 0;
78  std::ifstream file(name_);
79  std::string line;
80  while (getline(file, line)) {
81  // Update offset
82  offset++;
83 
84  // Try to find a /*CASADIMETA delimiter
85  std::string cmd = "/*CASADIMETA";
86  size_t pos = line.find(cmd);
87  if (pos != std::string::npos) {
88  read_meta(file, offset);
89  continue;
90  }
91 
92  // Try to find a /*CASADIEXTERNAL delimiter
93  cmd = "/*CASADIEXTERNAL";
94  pos = line.find(cmd);
95  if (pos != std::string::npos) {
96  std::istringstream ss(line.substr(pos+cmd.size()));
97  // Read name
98  std::string sym;
99  ss >> sym;
100  casadi_assert_dev(ss.good());
101  // Default attributes
102  bool inlined = false;
103 
104  // Read attributes: FIXME(@jaeandersson): Hacky
105  size_t eqpos = line.find('=', pos+cmd.size());
106  if (eqpos != std::string::npos) {
107  std::string attr = "inline";
108  if (line.compare(eqpos-attr.size(), attr.size(), attr)==0) {
109  casadi_assert_dev(line.size()>eqpos+1);
110  if (line.at(eqpos+1)=='1') {
111  inlined=true;
112  } else {
113  casadi_assert_dev(line.at(eqpos+1)=='0');
114  }
115  }
116  }
117 
118  read_external(sym, inlined, file, offset);
119  continue;
120  }
121  }
122  }
123 
124  // Read options
125  for (auto&& op : opts) {
126  if (op.first=="verbose") {
127  verbose_ = op.second;
128  }
129  }
130  }
131 
132  void ImporterInternal::read_meta(std::istream& file, casadi_int& offset) {
133  // Loop over the lines
134  std::string line;
135  while (getline(file, line)) {
136  offset++;
137 
138  // End of meta found?
139  if (line.find("*/") != std::string::npos) return;
140 
141  // If comment or empty line, skip
142  if (line.empty() || line.at(0)=='#') continue;
143 
144  // Get command string
145  casadi_assert(line.at(0)==':',
146  "Syntax error: " + line + " is not a command string");
147  std::string cmd = line.substr(1, line.find(' ')-1);
148 
149  // New entry
150  std::stringstream ss;
151 
152  // Collect the meta data
153  line = line.substr(cmd.size()+2);
154  while (true) {
155  // Find the backslash, if any
156  size_t stop = line.find('\\');
157 
158  // Add to entry
159  ss << line.substr(0, stop);
160 
161  // Break if not multiline
162  if (stop == std::string::npos) break;
163 
164  // Read another line
165  ss << std::endl;
166  if (!getline(file, line)) {
167  casadi_error("Failed to read \"" + cmd + "\"");
168  }
169  offset++;
170  }
171 
172  // Insert new element in map
173  auto new_el = meta_.insert(std::make_pair(cmd, std::make_pair(offset, ss.str())));
174  casadi_assert(new_el.second, "Duplicate entry: \"" + cmd + "\"");
175  }
176  casadi_error("End-of-file reached while searching for \"*/\"");
177  }
178 
180  read_external(const std::string& sym, bool inlined, std::istream& file, casadi_int& offset) {
181  // New entry
182  std::stringstream ss;
183 
184  // Are we still in the function declaration
185  bool in_declaration = true;
186 
187  // Loop over the lines
188  std::string line;
189  while (getline(file, line)) {
190  offset++;
191 
192  // Skip line if still in declaration
193  if (in_declaration) {
194  size_t stop = line.find('{');
195  if (stop != std::string::npos) in_declaration = false;
196  continue;
197  }
198 
199  // End of declaration found?
200  if (line.find("/*CASADIEXTERNAL") != std::string::npos) {
201  auto new_el = external_.insert(std::make_pair(sym, std::make_pair(inlined, ss.str())));
202  casadi_assert(new_el.second, "Duplicate symbol: \"" + sym + "\"");
203  return;
204  }
205 
206  // Add to entry
207  if (inlined) {
208  ss << line << std::endl;
209  }
210  }
211  casadi_error("End-of-file reached while searching for \"/*CASADIEXTERNAL\"");
212  }
213 
214  bool ImporterInternal::has_function(const std::string& symname) const {
215  // Check if in meta information
216  if (external_.find(symname)!=external_.end()) return true;
217 
218  // Convert to a dummy function pointer
219  return const_cast<ImporterInternal*>(this)->get_function(symname)!=nullptr;
220  }
221 
222  DllLibrary::DllLibrary(const std::string& bin_name)
223  : ImporterInternal(bin_name), handle_(nullptr) {
224 
225  }
226 
228 
229  std::vector<std::string> search_paths = get_search_paths();
230 
232  std::string dir = Filesystem::parent_path(name_);
233  // Does search path already contain the directory?
234  for (const std::string& path : search_paths) {
235  if (path==dir) {
236  dir = "";
237  break;
238  }
239  }
240  // Add directory to search path
241  if (!dir.empty()) search_paths.push_back(dir);
242  }
243 
244 #ifdef WITH_DL
245  handle_ = open_shared_library(name_, search_paths, "DllLibrary::init_handle");
246 #else // WITH_DL
247  casadi_error("CommonExternal: WITH_DL not activated");
248 #endif // WITH_DL
249  }
250 
252  init_handle();
253  }
254 
256 #ifdef WITH_DL
257  if (handle_) close_shared_library(handle_);
258 #endif // WITH_DL
259  }
260 
261  signal_t DllLibrary::get_function(const std::string& sym) {
262 #ifdef WITH_DL
263 #ifdef _WIN32
264 #if __GNUC__
265 #pragma GCC diagnostic push
266 #pragma GCC diagnostic ignored "-Wcast-function-type"
267 #endif
268  return reinterpret_cast<signal_t>(GetProcAddress(handle_, TEXT(sym.c_str())));
269 #if __GNUC__
270 #pragma GCC diagnostic pop
271 #endif
272 #else // _WIN32
273  signal_t fcnPtr = reinterpret_cast<signal_t>(dlsym(handle_, sym.c_str()));
274  if (dlerror()) {
275  fcnPtr=nullptr;
276  dlerror(); // Reset error flags
277  }
278  return fcnPtr;
279 #endif // _WIN32
280 #endif // WITH_DL
281  }
282 
283  std::string ImporterInternal::get_meta(const std::string& cmd, casadi_int ind) const {
284  if (ind>=0) return get_meta(indexed(cmd, ind));
285  casadi_assert(has_meta(cmd), "No such command: " + cmd);
286  return meta_.at(cmd).second;
287  }
288 
289  bool ImporterInternal::has_meta(const std::string& cmd, casadi_int ind) const {
290  if (ind>=0) return has_meta(indexed(cmd, ind));
291  return meta_.find(cmd) != meta_.end();
292  }
293 
294  bool ImporterInternal::inlined(const std::string& symname) const {
295  auto it = external_.find(symname);
296  return it!=external_.end() && it->second.first;
297  }
298 
299  std::string ImporterInternal::body(const std::string& symname) const {
300  auto it = external_.find(symname);
301  casadi_assert_dev(it!=external_.end() && it->second.first);
302  return it->second.second;
303  }
304 
305  std::string ImporterInternal::library() const {
306  casadi_error("library not implemented.");
307  }
308 
310  serialize_type(s);
311  serialize_body(s);
312  }
313 
315  s.pack("ImporterInternal::type", class_name());
316  }
317 
319  s.version("ImporterInternal", 1);
320  s.pack("ImporterInternal::name", name_);
321  s.pack("ImporterInternal::meta", meta_);
322  s.pack("ImporterInternal::external", external_);
323  }
324 
326  s.version("ImporterInternal", 1);
327  s.unpack("ImporterInternal::name", name_);
328  s.unpack("ImporterInternal::meta", meta_);
329  s.unpack("ImporterInternal::external", external_);
330  }
331 
333  std::string class_name;
334  s.unpack("ImporterInternal::type", class_name);
335  if (class_name=="DllLibrary") {
336  return DllLibrary::deserialize(s);
337  } else {
338  casadi_error("Cannot deserialize type '" + class_name + "'");
339  }
340  }
341 
343  DllLibrary* ret = new DllLibrary(s);
344  ret->finalize();
345  return ret;
346  }
347 
348  std::string DllLibrary::library() const {
349  return name_;
350  }
351 
352 } // namespace casadi
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
Dynamically linked library.
void finalize() override
std::string library() const override
Get library name.
signal_t get_function(const std::string &symname) override
Get a function pointer for numerical evaluation.
DllLibrary(const std::string &bin_name)
static ImporterInternal * deserialize(DeserializingStream &s)
static bool has_parent_path(const std::string &path)
Definition: filesystem.cpp:53
static std::string parent_path(const std::string &path)
Definition: filesystem.cpp:58
static bool is_enabled()
Definition: filesystem.cpp:83
Importer internal class.
~ImporterInternal() override
Destructor.
std::map< std::string, std::pair< casadi_int, std::string > > meta_
Meta data.
void read_meta(std::istream &file, casadi_int &offset)
Get meta information.
bool verbose_
Verbose – for debugging purposes.
bool inlined(const std::string &symname) const
virtual void init(const Dict &opts)
Initialize.
void serialize(SerializingStream &s) const
void disp(std::ostream &stream, bool more) const override
Print.
std::string get_meta(const std::string &cmd, casadi_int ind=-1) const
Get entry as a text.
ImporterInternal(const std::string &name)
Constructor.
virtual std::string library() const
Get library name.
std::string class_name() const override
Get type name.
virtual signal_t get_function(const std::string &symname)
Get a function pointer for numerical evaluation.
static const std::string infix_
Infix.
static const Options options_
Options.
bool has_meta(const std::string &cmd, casadi_int ind=-1) const
Does an entry exist?
virtual bool can_have_meta() const
Can meta information be read?
static std::string indexed(const std::string &cmd, casadi_int ind)
void construct(const Dict &opts)
Construct.
void read_external(const std::string &sym, bool inlined, std::istream &file, casadi_int &offset)
Get an external function declaration.
std::string body(const std::string &symname) const
Get the function body, if inlined.
bool has_function(const std::string &symname) const
Get a function pointer for numerical evaluation.
virtual void serialize_body(SerializingStream &s) const
static ImporterInternal * deserialize(DeserializingStream &s)
std::string name_
C filename.
virtual const Options & get_options() const
Options.
virtual void serialize_type(SerializingStream &s) const
std::map< std::string, std::pair< bool, std::string > > external_
External functions.
static std::map< std::string, Plugin > solvers_
Collection of solvers.
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
The casadi namespace.
Definition: archiver.cpp:28
std::vector< std::string > get_search_paths()
Definition: casadi_os.cpp:49
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void(* signal_t)(void)
Function pointer types for the C API.
std::vector< casadi_int > path(const std::vector< casadi_int > &map, casadi_int i_start)
Options metadata for a class.
Definition: options.hpp:40
static bool is_sane(const Dict &opts)
Is the dictionary sane.
Definition: options.cpp:169
static Dict sanitize(const Dict &opts, bool top_level=true)
Sanitize a options dictionary.
Definition: options.cpp:173
void check(const Dict &opts) const
Check if options exist.
Definition: options.cpp:240