]> www.ginac.de Git - ginac.git/blob - ginac/excompiler.cpp
parser: improve error reporting a little bit.
[ginac.git] / ginac / excompiler.cpp
1 /** @file excompiler.cpp
2  *
3  *  Functions to facilitate the conversion of a ex to a function pointer suited for
4  *  fast numerical integration.
5  *
6  */
7
8 /*
9  *  GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24  */
25
26 #include "excompiler.h"
27
28 #include <stdexcept>
29 #include <ios>
30 #include <fstream>
31 #include <sstream>
32 #include <string>
33 #include <vector>
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #ifdef HAVE_LIBDL
40 #include <dlfcn.h>
41 #endif // def HAVE_LIBDL
42
43 #include "ex.h"
44 #include "lst.h"
45 #include "operators.h"
46 #include "relational.h"
47 #include "symbol.h"
48
49 namespace GiNaC {
50
51 #ifdef HAVE_LIBDL
52         
53 /**
54  * Small class that manages modules opened by libdl. It is used by compile_ex
55  * and link_ex in order to have a clean-up of opened modules and their
56  * associated source and so-files at the time of program termination. It is
57  * supposed to be statically instantiated once (see definition of
58  * global_excompiler below). The dtor of that object then performs the
59  * clean-up. On top of that it provides some functionality shared between
60  * different compile_ex and link_ex specializations.
61  */
62 class excompiler
63 {
64         /**
65          * Holds all necessary information about opened modules.
66          */
67         struct filedesc
68         {
69                 void* module;
70                 std::string name; /**< filename with .so suffix */
71                 bool clean_up; /**< if true, source and so-file will be deleted */
72         };
73         std::vector<filedesc> filelist; /**< List of all opened modules */
74 public:
75         /**
76          * Complete clean-up of opend modules is done on destruction.
77          */
78         ~excompiler()
79         {
80                 for (std::vector<filedesc>::const_iterator it = filelist.begin(); it != filelist.end(); ++it) {
81                         clean_up(it);
82                 }
83         }
84         /**
85          * Adds a new module to the list.
86          */
87         void add_opened_module(void* module, const std::string& name, bool clean_up)
88         {
89                 filedesc fd;
90                 fd.module = module;
91                 fd.name = name;
92                 fd.clean_up = clean_up;
93                 filelist.push_back(fd);
94         }
95         /**
96          * Closes a module and deletes the so-file if the associated clean-up flag is true.
97          */
98         void clean_up(const std::vector<filedesc>::const_iterator it)
99         {
100                 dlclose(it->module);
101                 if (it->clean_up) {
102                         remove(it->name.c_str());
103                 }
104         }
105         /**
106          * Creates a new C source file and adds a standard header. If filename is
107          * empty, a unique random name is produced and used.
108          */
109         void create_src_file(std::string& filename, std::ofstream& ofs)
110         {
111                 if (filename.empty()) {
112                         // fill filename with unique random word
113                         const char* filename_pattern = "./GiNaCXXXXXX";
114                         char* new_filename = new char[strlen(filename_pattern)+1];
115                         strcpy(new_filename, filename_pattern);
116                         if (!mktemp(new_filename)) {
117                                 delete new_filename;
118                                 throw std::runtime_error("mktemp failed");
119                         }
120                         filename = std::string(new_filename);
121                         ofs.open(new_filename, std::ios::out);
122                         delete new_filename;
123                 } else {
124                         // use parameter as filename
125                         ofs.open(filename.c_str(), std::ios::out);
126                 }
127                 
128                 if (!ofs) {
129                         throw std::runtime_error("could not create source code file for compilation");
130                 }
131
132                 ofs << "#include <stddef.h> " << std::endl;
133                 ofs << "#include <stdlib.h> " << std::endl;
134                 ofs << "#include <math.h> " << std::endl;
135                 ofs << std::endl;
136         }
137         /**
138          * Calls the shell script 'ginac-excompiler' to compile the produced C
139          * source file into an linkable so-file.  On demand the C source file is
140          * deleted.
141          */
142         void compile_src_file(const std::string filename, bool clean_up)
143         {
144                 std::string strcompile = "ginac-excompiler " + filename;
145                 if (system(strcompile.c_str())) {
146                         throw std::runtime_error("excompiler::compile_src_file: error compiling source file!");
147                 }
148                 if (clean_up) {
149                         remove(filename.c_str());
150                 }
151         }
152         /**
153          * Links a so-file whose filename is given.
154          */
155         void* link_so_file(const std::string filename, bool clean_up)
156         {
157                 void* module = NULL;
158                 module = dlopen(filename.c_str(), RTLD_NOW);
159                 if (module == NULL)     {
160                         throw std::runtime_error("excompiler::link_so_file: could not open compiled module!");
161                 }
162
163                 add_opened_module(module, filename, clean_up);
164
165                 return dlsym(module, "compiled_ex");
166         }
167         /**
168          * Removes a modules from the module list. Performs a clean-up before that.
169          * Every module with the given name will be affected.
170          */
171         void unlink(const std::string filename)
172         {
173                 for (std::vector<filedesc>::iterator it = filelist.begin(); it != filelist.end();) {
174                         if (it->name == filename) {
175                                 clean_up(it);
176                                 filelist.erase(it);
177                         } else {
178                                 ++it;
179                         }
180                 }
181         }
182 };
183
184 /**
185  * This static object manages the modules opened by the complile_ex and link_ex
186  * functions. On program termination its dtor is called and all open modules
187  * are closed. The associated source and so-files are eventually deleted then
188  * as well.
189  * In principle this could lead to a static deconstruction order fiasco, if
190  * other code from this library uses the compile_ex and link_ex functions
191  * (which it doesn't at the moment and won't in the likely future, so therefore
192  * we ignore this issue).
193  */
194 static excompiler global_excompiler;
195
196 void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp, const std::string filename)
197 {
198         symbol x("x");
199         ex expr_with_x = expr.subs(lst(sym==x));
200
201         std::ofstream ofs;
202         std::string unique_filename = filename;
203         global_excompiler.create_src_file(unique_filename, ofs);
204
205         ofs << "double compiled_ex(double x)" << std::endl;
206         ofs << "{" << std::endl;
207         ofs << "double res = ";
208         expr_with_x.print(GiNaC::print_csrc_double(ofs));
209         ofs << ";" << std::endl;
210         ofs << "return(res); " << std::endl;
211         ofs << "}" << std::endl;
212
213         ofs.close();
214
215         global_excompiler.compile_src_file(unique_filename, filename.empty());
216         // This is not standard compliant! ... no conversion between
217         // pointer-to-functions and pointer-to-objects ...
218         fp = (FUNCP_1P) global_excompiler.link_so_file(unique_filename+".so", filename.empty());
219 }
220
221 void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2, FUNCP_2P& fp, const std::string filename)
222 {
223         symbol x("x"), y("y");
224         ex expr_with_xy = expr.subs(lst(sym1==x, sym2==y));
225
226         std::ofstream ofs;
227         std::string unique_filename = filename;
228         global_excompiler.create_src_file(unique_filename, ofs);
229
230         ofs << "double compiled_ex(double x, double y)" << std::endl;
231         ofs << "{" << std::endl;
232         ofs << "double res = ";
233         expr_with_xy.print(GiNaC::print_csrc_double(ofs));
234         ofs << ";" << std::endl;
235         ofs << "return(res); " << std::endl;
236         ofs << "}" << std::endl;
237
238         ofs.close();
239
240         global_excompiler.compile_src_file(unique_filename, filename.empty());
241         // This is not standard compliant! ... no conversion between
242         // pointer-to-functions and pointer-to-objects ...
243         fp = (FUNCP_2P) global_excompiler.link_so_file(unique_filename+".so", filename.empty());
244 }
245
246 void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp, const std::string filename)
247 {
248         lst replacements;
249         for (int count=0; count<syms.nops(); ++count) {
250                 std::ostringstream s;
251                 s << "a[" << count << "]";
252                 replacements.append(syms.op(count) == symbol(s.str()));
253         }
254
255         std::vector<ex> expr_with_cname;
256         for (int count=0; count<exprs.nops(); ++count) {
257                 expr_with_cname.push_back(exprs.op(count).subs(replacements));
258         }
259
260         std::ofstream ofs;
261         std::string unique_filename = filename;
262         global_excompiler.create_src_file(unique_filename, ofs);
263
264         ofs << "void compiled_ex(const int* an, const double a[], const int* fn, double f[])" << std::endl;
265         ofs << "{" << std::endl;
266         for (int count=0; count<exprs.nops(); ++count) {
267                 ofs << "f[" << count << "] = ";
268                 expr_with_cname[count].print(GiNaC::print_csrc_double(ofs));
269                 ofs << ";" << std::endl;
270         }
271         ofs << "}" << std::endl;
272
273         ofs.close();
274
275         global_excompiler.compile_src_file(unique_filename, filename.empty());
276         // This is not standard compliant! ... no conversion between
277         // pointer-to-functions and pointer-to-objects ...
278         fp = (FUNCP_CUBA) global_excompiler.link_so_file(unique_filename+".so", filename.empty());
279 }
280
281 void link_ex(const std::string filename, FUNCP_1P& fp)
282 {
283         // This is not standard compliant! ... no conversion between
284         // pointer-to-functions and pointer-to-objects ...
285         fp = (FUNCP_1P) global_excompiler.link_so_file(filename, false);
286 }
287
288 void link_ex(const std::string filename, FUNCP_2P& fp)
289 {
290         // This is not standard compliant! ... no conversion between
291         // pointer-to-functions and pointer-to-objects ...
292         fp = (FUNCP_2P) global_excompiler.link_so_file(filename, false);
293 }
294
295 void link_ex(const std::string filename, FUNCP_CUBA& fp)
296 {
297         // This is not standard compliant! ... no conversion between
298         // pointer-to-functions and pointer-to-objects ...
299         fp = (FUNCP_CUBA) global_excompiler.link_so_file(filename, false);
300 }
301
302 void unlink_ex(const std::string filename)
303 {
304         global_excompiler.unlink(filename);
305 }
306
307 #else // def HAVE_LIBDL
308
309 /*
310  * In case no working libdl has been found by configure, the following function
311  * stubs preserve the interface. Every function just raises an exception.
312  */
313
314 void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp, const std::string filename)
315 {
316         throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
317 }
318
319 void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2, FUNCP_2P& fp, const std::string filename)
320 {
321         throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
322 }
323
324 void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp, const std::string filename)
325 {
326         throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
327 }
328
329 void link_ex(const std::string filename, FUNCP_1P& fp)
330 {
331         throw std::runtime_error("link_ex has been disabled because of missing libdl!");
332 }
333
334 void link_ex(const std::string filename, FUNCP_2P& fp)
335 {
336         throw std::runtime_error("link_ex has been disabled because of missing libdl!");
337 }
338
339 void link_ex(const std::string filename, FUNCP_CUBA& fp)
340 {
341         throw std::runtime_error("link_ex has been disabled because of missing libdl!");
342 }
343
344 void unlink_ex(const std::string filename)
345 {
346         throw std::runtime_error("unlink_ex has been disabled because of missing libdl!");
347 }
348
349 #endif // def HAVE_LIBDL
350
351 } // namespace GiNaC