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