New interface and complete source.
authorJens Vollinga <vollinga@thep.physik.uni-mainz.de>
Wed, 30 Aug 2006 20:07:27 +0000 (20:07 +0000)
committerJens Vollinga <vollinga@thep.physik.uni-mainz.de>
Wed, 30 Aug 2006 20:07:27 +0000 (20:07 +0000)
ginac/excompiler.cpp
ginac/excompiler.h

index 8e586536f7cc0d08f6f4fdc8f46f51adda22092e..30b33066e66f382eeb476f7b8ff18c8f02d8213b 100644 (file)
@@ -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-2006 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
 
 #include "excompiler.h"
 
-#include <dlfcn.h>
 #include <stdexcept>
+#include <ios>
 #include <fstream>
 #include <sstream>
 #include <string>
 #include <vector>
 
 #include "config.h"
+
+#ifdef HAVE_LIBDL
+#include <dlfcn.h>
+#endif // def HAVE_LIBDL
+
 #include "ex.h"
 #include "lst.h"
 #include "operators.h"
@@ -41,51 +48,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<filedesc> filelist; /**< List of all opened modules */
 public:
-       excompiler() {};
+       /**
+        * Complete clean-up of opend modules is done on destruction.
+        */
        ~excompiler()
        {
                for (std::vector<filedesc>::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<filedesc> filelist;
-};
+       /**
+        * Closes a module and deletes the so-file if the associated clean-up flag is true.
+        */
+       void clean_up(const std::vector<filedesc>::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");
+               }
 
-excompiler _exc;
+               ofs << "#include <stddef.h> " << std::endl;
+               ofs << "#include <stdlib.h> " << std::endl;
+               ofs << "#include <math.h> " << 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!");
+               }
 
-FP_dim1 compile(const ex& expr, const symbol& sym)
-{
-       symbol argx("argx");
-       ex expr_with_x = expr.subs(lst(sym==argx));
+               add_opened_module(module, filename, clean_up);
 
-       char filename[] = "/tmp/GiNaCXXXXXX";
+               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<filedesc>::iterator it = filelist.begin(); it != filelist.end();) {
+                       if (it->name == filename) {
+                               clean_up(it);
+                               filelist.erase(it);
+                       } else {
+                               ++it;
+                       }
+               }
+       }
+};
+
+/**
+ * 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 <stddef.h> " << std::endl;
-       ofs << "#include <stdlib.h> " << std::endl;
-       ofs << "#include <math.h> " << 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));
@@ -95,25 +210,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());
+}
+
+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 strremove = "rm " + std::string(filename) + " " + std::string(filename) + ".o";
-       system(strremove.c_str());
+       std::ofstream ofs;
+       std::string unique_filename = filename;
+       global_excompiler.create_src_file(unique_filename, ofs);
 
-       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!");
-       }
+       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;
 
-       _exc.add(module, strsofile);
+       ofs.close();
 
-       return (FP_dim1) dlsym(module, "compiled_ex");
+       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<syms.nops(); ++count) {
@@ -127,16 +255,10 @@ FP_cuba compile(const lst& exprs, const lst& syms)
                expr_with_cname.push_back(exprs.op(count).subs(replacements));
        }
 
-       char filename[] = "/tmp/GiNaCXXXXXX";
-
-       int fno = mkstemp(filename);
+       std::ofstream ofs;
+       std::string unique_filename = filename;
+       global_excompiler.create_src_file(unique_filename, ofs);
 
-       std::ofstream ofs(filename);
-
-       ofs << "#include <stddef.h> " << std::endl;
-       ofs << "#include <stdlib.h> " << std::endl;
-       ofs << "#include <math.h> " << 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<exprs.nops(); ++count) {
@@ -148,38 +270,80 @@ FP_cuba compile(const lst& exprs, const lst& syms)
 
        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_CUBA) 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 link_ex(const std::string filename, FUNCP_1P& fp)
+{
+       // This is not standard compliant! ... no conversion between
+       // pointer-to-functions and pointer-to-objects ...
+       fp = (FUNCP_1P) global_excompiler.link_so_file(filename, false);
+}
 
-       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!");
-       }
+void link_ex(const std::string filename, FUNCP_2P& fp)
+{
+       // This is not standard compliant! ... no conversion between
+       // pointer-to-functions and pointer-to-objects ...
+       fp = (FUNCP_2P) global_excompiler.link_so_file(filename, false);
+}
 
-       _exc.add(module, strsofile);
+void link_ex(const std::string filename, FUNCP_CUBA& fp)
+{
+       // This is not standard compliant! ... no conversion between
+       // pointer-to-functions and pointer-to-objects ...
+       fp = (FUNCP_CUBA) global_excompiler.link_so_file(filename, false);
+}
 
-       return (FP_cuba) dlsym(module, "compiled_ex");
+void unlink_ex(const std::string filename)
+{
+       global_excompiler.unlink(filename);
 }
 
-#elif
+#elif // def HAVE_LIBDL
+
+/*
+ * In case no working libdl has been found by configure, the following function
+ * stubs preserve the interface. Every function just raises an exception.
+ */
+
+void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp, const std::string filename)
+{
+       throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
+}
+
+void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2, FUNCP_2P& fp, const std::string filename)
+{
+       throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
+}
+
+void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp, const std::string filename)
+{
+       throw std::runtime_error("compile_ex has been disabled because of missing libdl!");
+}
+
+void link_ex(const std::string filename, FUNCP_1P& fp)
+{
+       throw std::runtime_error("link_ex has been disabled because of missing libdl!");
+}
+
+void link_ex(const std::string filename, FUNCP_2P& fp)
+{
+       throw std::runtime_error("link_ex has been disabled because of missing libdl!");
+}
 
-FP_dim1 compile(const ex& expr, const symbol& sym)
+void link_ex(const std::string filename, FUNCP_CUBA& fp)
 {
-       throw std::runtime_error("compile has been disabled because of missing libdl!");
-       return NULL;
+       throw std::runtime_error("link_ex has been disabled because of missing libdl!");
 }
 
-FP_cuba compile(const lst& exprs, const lst& syms)
+void unlink_ex(const std::string filename)
 {
-       throw std::runtime_error("compile has been disabled because of missing libdl!");
-       return NULL;
+       throw std::runtime_error("unlink_ex has been disabled because of missing libdl!");
 }
 
-#endif
+#endif // def HAVE_LIBDL
 
 } // namespace GiNaC
index 56c870e14b813b4190ecbce2ba897926a12d1b4b..b31f753b376935c8a4219636bd7ed6fe9b150f04 100644 (file)
@@ -1,7 +1,9 @@
 /** @file excompiler.h
  *
- *  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-2006 Johannes Gutenberg University Mainz, Germany
 #ifndef __GINAC_EXCOMPILER_H__
 #define __GINAC_EXCOMPILER_H__
 
-#include "basic.h"
-#include "ex.h"
+#include <string>
+
+#include "lst.h"
 
 namespace GiNaC {
 
+class ex;
+class symbol;
 
-typedef double (*FP_dim1) (double);
+/**
+ * Function pointer with one function parameter.
+ */
+typedef double (*FUNCP_1P) (double);
 
-FP_dim1 compile(const ex& expr, const symbol& sym);
+/**
+ * Function pointer with two function parameters.
+ */
+typedef double (*FUNCP_2P) (double, double);
 
-typedef void (*FP_cuba) (const int*, const double[], const int*, double[]);
+/**
+ * Function pointer for use with the CUBA library (http://www.feynarts.de/cuba).
+ */
+typedef void (*FUNCP_CUBA) (const int*, const double[], const int*, double[]);
 
-FP_cuba compile(const lst& exprs, const lst& syms);
+/**
+ * Takes an expression and produces a function pointer to the compiled and linked
+ * C code equivalent in double precision. The function pointer has type FUNCP_1P.
+ *
+ * @param expr Expression to be compiled
+ * @param sym Symbol from the expression to become the function parameter
+ * @param fp Returned function pointer
+ * @param filename Name of the intermediate source code and so-file. If
+ * supplied, these intermediate files will not be deleted
+ */
+void compile_ex(const ex& expr, const symbol& sym, FUNCP_1P& fp, const std::string filename = "");
+
+/**
+ * Takes an expression and produces a function pointer to the compiled and linked
+ * C code equivalent in double precision. The function pointer has type FUNCP_2P.
+ *
+ * @param expr Expression to be compiled
+ * @param sym Symbol from the expression to become the function parameter
+ * @param fp Returned function pointer
+ * @param filename Name of the intermediate source code and so-file. If
+ * supplied, these intermediate files will not be deleted
+ */
+void compile_ex(const ex& expr, const symbol& sym1, const symbol& sym2, FUNCP_2P& fp, const std::string filename = "");
 
+/**
+ * Takes an expression and produces a function pointer to the compiled and linked
+ * C code equivalent in double precision. The function pointer has type FUNCP_CUBA.
+ *
+ * @param expr Expression to be compiled
+ * @param sym Symbol from the expression to become the function parameter
+ * @param fp Returned function pointer
+ * @param filename Name of the intermediate source code and so-file. If
+ * supplied, these intermediate files will not be deleted
+ */
+void compile_ex(const lst& exprs, const lst& syms, FUNCP_CUBA& fp, const std::string filename = "");
+
+/** 
+ * Opens an existing so-file and returns a function pointer of type FUNCP_1P to
+ * the contained function. The so-file has to be generated by compile_ex in
+ * advance.
+ *
+ * @param filename Name of the so-file to open and link
+ * @param fp Returned function pointer
+ */
+void link_ex(const std::string filename, FUNCP_1P& fp);
+
+/** 
+ * Opens an existing so-file and returns a function pointer of type FUNCP_2P to
+ * the contained function. The so-file has to be generated by compile_ex in
+ * advance.
+ *
+ * @param filename Name of the so-file to open and link
+ * @param fp Returned function pointer
+ */
+void link_ex(const std::string filename, FUNCP_2P& fp);
+
+/** 
+ * Opens an existing so-file and returns a function pointer of type FUNCP_CUBA to
+ * the contained function. The so-file has to be generated by compile_ex in
+ * advance.
+ *
+ * @param filename Name of the so-file to open and link
+ * @param fp Returned function pointer
+ */
+void link_ex(const std::string filename, FUNCP_CUBA& fp);
+
+/**
+ * Closes all linked .so files that have the supplied filename.
+ *
+ * @param filename Name of the so-file to close
+ */
+void unlink_ex(const std::string filename);
 
 } // namespace GiNaC