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
62  return;
63  }
64 
65  // Make sure all options exist
66  get_options().check(opts);
67 
68  // Initialize object
69  init(opts);
70 
71  // Revisit class hierarchy in reverse order
72  finalize();
73  }
74 
75  void ImporterInternal::init(const Dict& opts) {
76  // Read meta information from file
77  if (can_have_meta()) {
78  casadi_int offset = 0;
79  auto file_ptr = Filesystem::ifstream_ptr(name_);
80  std::istream& file = *file_ptr;
81 
82  std::string line;
83  while (getline(file, line)) {
84  // Update offset
85  offset++;
86 
87  // Try to find a /*CASADIMETA delimiter
88  std::string cmd = "/*CASADIMETA";
89  size_t pos = line.find(cmd);
90  if (pos != std::string::npos) {
91  read_meta(file, offset);
92  continue;
93  }
94 
95  // Try to find a /*CASADIEXTERNAL delimiter
96  cmd = "/*CASADIEXTERNAL";
97  pos = line.find(cmd);
98  if (pos != std::string::npos) {
99  std::istringstream ss(line.substr(pos+cmd.size()));
100  // Read name
101  std::string sym;
102  ss >> sym;
103  casadi_assert_dev(ss.good());
104  // Default attributes
105  bool inlined = false;
106 
107  // Read attributes: FIXME(@jaeandersson): Hacky
108  size_t eqpos = line.find('=', pos+cmd.size());
109  if (eqpos != std::string::npos) {
110  std::string attr = "inline";
111  if (line.compare(eqpos-attr.size(), attr.size(), attr)==0) {
112  casadi_assert_dev(line.size()>eqpos+1);
113  if (line.at(eqpos+1)=='1') {
114  inlined=true;
115  } else {
116  casadi_assert_dev(line.at(eqpos+1)=='0');
117  }
118  }
119  }
120 
121  read_external(sym, inlined, file, offset);
122  continue;
123  }
124  }
125  }
126 
127  // Read options
128  for (auto&& op : opts) {
129  if (op.first=="verbose") {
130  verbose_ = op.second;
131  }
132  }
133  }
134 
135  void ImporterInternal::read_meta(std::istream& file, casadi_int& offset) {
136  // Loop over the lines
137  std::string line;
138  while (getline(file, line)) {
139  offset++;
140 
141  // End of meta found?
142  if (line.find("*/") != std::string::npos) return;
143 
144  // If comment or empty line, skip
145  if (line.empty() || line.at(0)=='#') continue;
146 
147  // Get command string
148  casadi_assert(line.at(0)==':',
149  "Syntax error: " + line + " is not a command string");
150  std::string cmd = line.substr(1, line.find(' ')-1);
151 
152  // New entry
153  std::stringstream ss;
154 
155  // Collect the meta data
156  line = line.substr(cmd.size()+2);
157  while (true) {
158  // Find the backslash, if any
159  size_t stop = line.find('\\');
160 
161  // Add to entry
162  ss << line.substr(0, stop);
163 
164  // Break if not multiline
165  if (stop == std::string::npos) break;
166 
167  // Read another line
168  ss << std::endl;
169  if (!getline(file, line)) {
170  casadi_error("Failed to read \"" + cmd + "\"");
171  }
172  offset++;
173  }
174 
175  // Insert new element in map
176  auto new_el = meta_.insert(std::make_pair(cmd, std::make_pair(offset, ss.str())));
177  casadi_assert(new_el.second, "Duplicate entry: \"" + cmd + "\"");
178  }
179  casadi_error("End-of-file reached while searching for \"*/\"");
180  }
181 
183  read_external(const std::string& sym, bool inlined, std::istream& file, casadi_int& offset) {
184  // New entry
185  std::stringstream ss;
186 
187  // Are we still in the function declaration
188  bool in_declaration = true;
189 
190  // Loop over the lines
191  std::string line;
192  while (getline(file, line)) {
193  offset++;
194 
195  // Skip line if still in declaration
196  if (in_declaration) {
197  size_t stop = line.find('{');
198  if (stop != std::string::npos) in_declaration = false;
199  continue;
200  }
201 
202  // End of declaration found?
203  if (line.find("/*CASADIEXTERNAL") != std::string::npos) {
204  auto new_el = external_.insert(std::make_pair(sym, std::make_pair(inlined, ss.str())));
205  casadi_assert(new_el.second, "Duplicate symbol: \"" + sym + "\"");
206  return;
207  }
208 
209  // Add to entry
210  if (inlined) {
211  ss << line << std::endl;
212  }
213  }
214  casadi_error("End-of-file reached while searching for \"/*CASADIEXTERNAL\"");
215  }
216 
217  bool ImporterInternal::has_function(const std::string& symname) const {
218  // Check if in meta information
219  if (external_.find(symname)!=external_.end()) return true;
220 
221  // Convert to a dummy function pointer
222  return const_cast<ImporterInternal*>(this)->get_function(symname)!=nullptr;
223  }
224 
225  DllLibrary::DllLibrary(const std::string& bin_name)
226  : ImporterInternal(bin_name), handle_(nullptr) {
227 
228  }
229 
231 
232  std::vector<std::string> search_paths = get_search_paths();
233 
235  std::string dir = Filesystem::parent_path(name_);
236  // Does search path already contain the directory?
237  for (const std::string& path : search_paths) {
238  if (path==dir) {
239  dir = "";
240  break;
241  }
242  }
243  // Add directory to search path
244  if (!dir.empty()) search_paths.push_back(dir);
245  }
246 
247 #ifdef WITH_DL
248  handle_ = open_shared_library(name_, search_paths, "DllLibrary::init_handle");
249 #else // WITH_DL
250  casadi_error("CommonExternal: WITH_DL not activated");
251 #endif // WITH_DL
252  }
253 
255  init_handle();
256  }
257 
259 #ifdef WITH_DL
260  if (handle_) close_shared_library(handle_);
261 #endif // WITH_DL
262  }
263 
264  signal_t DllLibrary::get_function(const std::string& sym) {
265 #ifdef WITH_DL
266 #ifdef _WIN32
267 #if __GNUC__
268 #pragma GCC diagnostic push
269 #pragma GCC diagnostic ignored "-Wcast-function-type"
270 #endif
271  return reinterpret_cast<signal_t>(GetProcAddress(handle_, TEXT(sym.c_str())));
272 #if __GNUC__
273 #pragma GCC diagnostic pop
274 #endif
275 #else // _WIN32
276  signal_t fcnPtr = reinterpret_cast<signal_t>(dlsym(handle_, sym.c_str()));
277  if (dlerror()) {
278  fcnPtr=nullptr;
279  dlerror(); // Reset error flags
280  }
281  return fcnPtr;
282 #endif // _WIN32
283 #else // WITH_DL
284  (void)sym;
285  casadi_error("DllLibrary::get_function: WITH_DL option needed for dynamic loading");
286  return nullptr;
287 #endif // WITH_DL
288  }
289 
290  std::string ImporterInternal::get_meta(const std::string& cmd, casadi_int ind) const {
291  if (ind>=0) return get_meta(indexed(cmd, ind));
292  casadi_assert(has_meta(cmd), "No such command: " + cmd);
293  return meta_.at(cmd).second;
294  }
295 
296  bool ImporterInternal::has_meta(const std::string& cmd, casadi_int ind) const {
297  if (ind>=0) return has_meta(indexed(cmd, ind));
298  return meta_.find(cmd) != meta_.end();
299  }
300 
301  bool ImporterInternal::inlined(const std::string& symname) const {
302  auto it = external_.find(symname);
303  return it!=external_.end() && it->second.first;
304  }
305 
306  std::string ImporterInternal::body(const std::string& symname) const {
307  auto it = external_.find(symname);
308  casadi_assert_dev(it!=external_.end() && it->second.first);
309  return it->second.second;
310  }
311 
312  std::string ImporterInternal::library() const {
313  casadi_error("library not implemented.");
314  }
315 
317  serialize_type(s);
318  serialize_body(s);
319  }
320 
322  s.pack("ImporterInternal::type", class_name());
323  }
324 
326  s.version("ImporterInternal", 1);
327  s.pack("ImporterInternal::name", name_);
328  s.pack("ImporterInternal::meta", meta_);
329  s.pack("ImporterInternal::external", external_);
330  }
331 
333  s.version("ImporterInternal", 1);
334  s.unpack("ImporterInternal::name", name_);
335  s.unpack("ImporterInternal::meta", meta_);
336  s.unpack("ImporterInternal::external", external_);
337  }
338 
340  std::string class_name;
341  s.unpack("ImporterInternal::type", class_name);
342  if (class_name=="DllLibrary") {
343  return DllLibrary::deserialize(s);
344  } else {
345  casadi_error("Cannot deserialize type '" + class_name + "'");
346  }
347  }
348 
350  DllLibrary* ret = new DllLibrary(s);
351  ret->finalize();
352  return ret;
353  }
354 
355  std::string DllLibrary::library() const {
356  return name_;
357  }
358 
359 } // 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
static std::unique_ptr< std::istream > ifstream_ptr(const std::string &path, std::ios_base::openmode mode=std::ios_base::in, bool fail=true)
Definition: filesystem.cpp:135
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:79
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