X-Git-Url: https://www.ginac.de/ginac.git//ginac.git?p=ginac.git;a=blobdiff_plain;f=ginac%2Fexcompiler.cpp;h=1b4e73d6b33082301b393a3611909d10d7e4b73d;hp=4f121d7ac8ac30bcaa0dff84c5cd3ddb272bc5fd;hb=adb1dbd383ae6e5a999b5f8cba72a5c2bfd50c11;hpb=541c2950a84f2e0a19edae0da44ad2d317824ec4 diff --git a/ginac/excompiler.cpp b/ginac/excompiler.cpp index 4f121d7a..1b4e73d6 100644 --- a/ginac/excompiler.cpp +++ b/ginac/excompiler.cpp @@ -1,10 +1,12 @@ /** @file excompiler.cpp * - * Class to facilitate the conversion of a ex to a function pointer suited for - * fast numerical integration. */ + * Functions to facilitate the conversion of a ex to a function pointer suited for + * fast numerical integration. + * + */ /* - * GiNaC Copyright (C) 1999-2005 Johannes Gutenberg University Mainz, Germany + * GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,13 +25,21 @@ #include "excompiler.h" -#include #include +#include #include #include #include #include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_LIBDL +#include +#endif // def HAVE_LIBDL + #include "ex.h" #include "lst.h" #include "operators.h" @@ -40,51 +50,159 @@ namespace GiNaC { #ifdef HAVE_LIBDL +/** + * Small class that manages modules opened by libdl. It is used by compile_ex + * and link_ex in order to have a clean-up of opened modules and their + * associated source and so-files at the time of program termination. It is + * supposed to be statically instantiated once (see definition of + * global_excompiler below). The dtor of that object then performs the + * clean-up. On top of that it provides some functionality shared between + * different compile_ex and link_ex specializations. + */ class excompiler { + /** + * Holds all necessary information about opened modules. + */ struct filedesc { void* module; - std::string name; + std::string name; /**< filename with .so suffix */ + bool clean_up; /**< if true, source and so-file will be deleted */ }; + std::vector filelist; /**< List of all opened modules */ public: - excompiler() {}; + /** + * Complete clean-up of opend modules is done on destruction. + */ ~excompiler() { for (std::vector::const_iterator it = filelist.begin(); it != filelist.end(); ++it) { - dlclose(it->module); - std::string strremove = "rm " + it->name; - system(strremove.c_str()); + clean_up(it); } } - void add(void* module, std::string name) + /** + * Adds a new module to the list. + */ + void add_opened_module(void* module, const std::string& name, bool clean_up) { filedesc fd; fd.module = module; fd.name = name; + fd.clean_up = clean_up; filelist.push_back(fd); } - std::vector filelist; -}; + /** + * Closes a module and deletes the so-file if the associated clean-up flag is true. + */ + void clean_up(const std::vector::const_iterator it) + { + dlclose(it->module); + if (it->clean_up) { + remove(it->name.c_str()); + } + } + /** + * Creates a new C source file and adds a standard header. If filename is + * empty, a unique random name is produced and used. + */ + void create_src_file(std::string& filename, std::ofstream& ofs) + { + if (filename.empty()) { + // fill filename with unique random word + const char* filename_pattern = "./GiNaCXXXXXX"; + char* new_filename = new char[strlen(filename_pattern)+1]; + strcpy(new_filename, filename_pattern); + if (!mktemp(new_filename)) { + delete new_filename; + throw std::runtime_error("mktemp failed"); + } + filename = std::string(new_filename); + ofs.open(new_filename, std::ios::out); + delete new_filename; + } else { + // use parameter as filename + ofs.open(filename.c_str(), std::ios::out); + } + + if (!ofs) { + throw std::runtime_error("could not create source code file for compilation"); + } + + ofs << "#include " << std::endl; + ofs << "#include " << std::endl; + ofs << "#include " << std::endl; + ofs << std::endl; + } + /** + * Calls the shell script 'ginac-excompiler' to compile the produced C + * source file into an linkable so-file. On demand the C source file is + * deleted. + */ + void compile_src_file(const std::string filename, bool clean_up) + { + std::string strcompile = "ginac-excompiler " + filename; + if (system(strcompile.c_str())) { + throw std::runtime_error("excompiler::compile_src_file: error compiling source file!"); + } + if (clean_up) { + remove(filename.c_str()); + } + } + /** + * Links a so-file whose filename is given. + */ + void* link_so_file(const std::string filename, bool clean_up) + { + void* module = NULL; + module = dlopen(filename.c_str(), RTLD_NOW); + if (module == NULL) { + throw std::runtime_error("excompiler::link_so_file: could not open compiled module!"); + } -excompiler _exc; + add_opened_module(module, filename, clean_up); -FP_dim1 compile(const ex& expr, const symbol& sym) -{ - symbol argx("argx"); - ex expr_with_x = expr.subs(lst(sym==argx)); + return dlsym(module, "compiled_ex"); + } + /** + * Removes a modules from the module list. Performs a clean-up before that. + * Every module with the given name will be affected. + */ + void unlink(const std::string filename) + { + for (std::vector::iterator it = filelist.begin(); it != filelist.end();) { + if (it->name == filename) { + clean_up(it); + filelist.erase(it); + } else { + ++it; + } + } + } +}; - char filename[] = "/tmp/GiNaCXXXXXX"; +/** + * This static object manages the modules opened by the complile_ex and link_ex + * functions. On program termination its dtor is called and all open modules + * are closed. The associated source and so-files are eventually deleted then + * as well. + * In principle this could lead to a static deconstruction order fiasco, if + * other code from this library uses the compile_ex and link_ex functions + * (which it doesn't at the moment and won't in the likely future, so therefore + * we ignore this issue). + */ +static excompiler global_excompiler; - int fno = mkstemp(filename); +void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp, const std::string filename) +{ + symbol x("x"); + ex expr_with_x = expr.subs(lst(sym==x)); - std::ofstream ofs(filename); + std::ofstream ofs; + std::string unique_filename = filename; + global_excompiler.create_src_file(unique_filename, ofs); - ofs << "#include " << std::endl; - ofs << "#include " << std::endl; - ofs << "#include " << std::endl; - ofs << std::endl; - ofs << "double compiled_ex(double argx)" << std::endl; + ofs << "double compiled_ex(double x)" << std::endl; ofs << "{" << std::endl; ofs << "double res = "; expr_with_x.print(GiNaC::print_csrc_double(ofs)); @@ -94,25 +212,38 @@ FP_dim1 compile(const ex& expr, const symbol& sym) ofs.close(); - std::string strcompile = "ginac-excompiler " + std::string(filename); - system(strcompile.c_str()); + global_excompiler.compile_src_file(unique_filename, filename.empty()); + // This is not standard compliant! ... no conversion between + // pointer-to-functions and pointer-to-objects ... + fp = (FUNCP_1P) global_excompiler.link_so_file(unique_filename+".so", filename.empty()); +} - std::string strremove = "rm " + std::string(filename) + " " + std::string(filename) + ".o"; - system(strremove.c_str()); +void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2, FUNCP_2P& fp, const std::string filename) +{ + symbol x("x"), y("y"); + ex expr_with_xy = expr.subs(lst(sym1==x, sym2==y)); - std::string strsofile = std::string(filename) + ".so"; - void* module = NULL; - module = dlopen(strsofile.c_str(), RTLD_NOW); - if (module == NULL) { - throw std::runtime_error("excompiler: could not open compiled module!"); - } + std::ofstream ofs; + std::string unique_filename = filename; + global_excompiler.create_src_file(unique_filename, ofs); - _exc.add(module, strsofile); + ofs << "double compiled_ex(double x, double y)" << std::endl; + ofs << "{" << std::endl; + ofs << "double res = "; + expr_with_xy.print(GiNaC::print_csrc_double(ofs)); + ofs << ";" << std::endl; + ofs << "return(res); " << std::endl; + ofs << "}" << std::endl; - return (FP_dim1) dlsym(module, "compiled_ex"); + ofs.close(); + + global_excompiler.compile_src_file(unique_filename, filename.empty()); + // This is not standard compliant! ... no conversion between + // pointer-to-functions and pointer-to-objects ... + fp = (FUNCP_2P) global_excompiler.link_so_file(unique_filename+".so", filename.empty()); } -FP_cuba compile(const lst& exprs, const lst& syms) +void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp, const std::string filename) { lst replacements; for (int count=0; count " << std::endl; - ofs << "#include " << std::endl; - ofs << "#include " << std::endl; - ofs << std::endl; ofs << "void compiled_ex(const int* an, const double a[], const int* fn, double f[])" << std::endl; ofs << "{" << std::endl; for (int count=0; count