/** @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"
#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));
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) {
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) {
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
/** @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