From ecf5a85958414247384677c2448c0ac9a769538b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carsten=20Gr=C3=A4ser?= <graeser@dune-project.org> Date: Fri, 11 Jul 2014 00:07:26 +0200 Subject: [PATCH] Add class Callable that encapsulates callable function objects This class implements the various call() method that have been on Reference before. Notice that these will be removed from Reference. --- dune/fufem/python/CMakeLists.txt | 1 + dune/fufem/python/Makefile.am | 1 + dune/fufem/python/callable.hh | 228 +++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 dune/fufem/python/callable.hh diff --git a/dune/fufem/python/CMakeLists.txt b/dune/fufem/python/CMakeLists.txt index 7b2ddb2e..e95e4339 100644 --- a/dune/fufem/python/CMakeLists.txt +++ b/dune/fufem/python/CMakeLists.txt @@ -1,4 +1,5 @@ install(FILES + callable.hh common.hh conversion.hh function.hh diff --git a/dune/fufem/python/Makefile.am b/dune/fufem/python/Makefile.am index 4e3428af..15c31ed4 100644 --- a/dune/fufem/python/Makefile.am +++ b/dune/fufem/python/Makefile.am @@ -5,6 +5,7 @@ SUBDIRS = pythondir = $(includedir)/dune/fufem/python python_HEADERS = \\ + callable.hh \\ common.hh \\ conversion.hh \\ function.hh \\ diff --git a/dune/fufem/python/callable.hh b/dune/fufem/python/callable.hh new file mode 100644 index 00000000..9d014317 --- /dev/null +++ b/dune/fufem/python/callable.hh @@ -0,0 +1,228 @@ +// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set ts=8 sw=4 et sts=4: +#ifndef DUNE_FUFEM_PYTHON_CALLABLE_HH +#define DUNE_FUFEM_PYTHON_CALLABLE_HH + +// Only introduce the dune-python interface if python +// was found and enabled. +#if HAVE_PYTHON + +#include <Python.h> + +#include <string> +#include <sstream> +#include <vector> +#include <map> + +#include <dune/common/exceptions.hh> +#include <dune/common/shared_ptr.hh> +#include <dune/common/fvector.hh> +#include <dune/common/function.hh> +#include <dune/common/parametertree.hh> +#include <dune/common/typetraits.hh> + +#include <dune/grid/common/gridfactory.hh> + +#include <dune/fufem/functions/virtualfunctiontoboundarysegmentadapter.hh> +#include <dune/fufem/formatstring.hh> + + +#include <dune/fufem/python/reference.hh> + + +namespace Python +{ + +// forward declarations +void handlePythonError(const std::string&, const std::string&); + + + +/** + * \brief Wrapper class for callable python objects + * + * This class derives from Python::Reference and + * encapsulates functionality of callable objects. + */ +class Callable : + public Reference +{ + public: + + /** + * \brief Construct empty Callable + */ + Callable() : + Reference() + {} + + /** + * \brief Construct Callable from PyObject* + * + * Only to be used if you want to extend the interface + * using the python api. + * + * This forwards to the corresponding constructor of Reference + * and then checks if the python object is callable. If this is + * not the case an exception is thrown. As a consequence the + * reference count of the PyObject will be correctly decreased by + * ~Reference even if the exception is thrown. + * + * But be carefull to always increment the count of a borrowed + * reference BEFORE calling this constructor. I.e. always use + * + * Callable(Imp::inc(p)) + * + * instead of + * + * Imp::inc(Callable(p)) + * + * The latter may throw an exception and then decrease the count + * before it is increased. + * + */ + Callable(PyObject* p) : + Reference(p) + { + assertCallable(p_, "Callable(PyObject*)"); + } + + /** + * \brief Construct Callable from Reference + * + * This checks if the python object is callable. If this is + * not the case an exception is thrown. + * + * For implementors: + * + * This will increment the count for the + * stored reference. + */ + Callable(const Reference& other) : + Reference(other) + { + assertCallable(p_, "Callable(Reference&)"); + } + + /** + * \brief Destructor + */ + virtual ~Callable() + {} + + /** + * \brief Assignment + * + * This will checks if the python object is a module. + * If this is not the case an exception is thrown. + */ + virtual Callable& operator= (const Reference& other) + { + assertCallable(other, "Callable::operator=(Reference&)"); + Reference::operator=(other); + return *this; + } + + /** + * \brief Call this Reference with arguments given as tuple + * + * If the Reference represents a function it's called. + * If the Reference represents an instance its __call__ method is invoked. + * If the Reference represents a class a constructor is invoked. + * In any case the arguments are given as a tuple as obtained by the + * global method Python::makeTuple(). + * + * Although the method is const it might change the refered object! + * This cannot be avoided since the python api does not know + * about const pointers so we use a mutable pointer internally. + * + * \param args Arguments represented as tuple + * \returns The result of the call + * + */ + Reference callWithArgumentTuple(const Reference& args) const + { + assertPyObject("Callable::callWithArgumentTuple()"); + PyObject* result = PyObject_CallObject(p_, args); + if (not result) + handlePythonError("Callable::callWithArgumentTuple()", "failed to call object"); + return result; + } + + Reference call(const Reference& args) const DUNE_DEPRECATED + { + return callWithArgumentTuple(args); + } + + /** + * \brief Call this object without arguments + * + * Shortcut for callWithArgumentTuple(makeTuple()). + */ + Reference operator() () const + { + return callWithArgumentTuple(makeTuple()); + } + + /** + * \brief Call this object with one argument + * + * Shortcut for callWithArgumentTuple(makeTuple(t0)). + */ + template<class T0> + Reference operator() (const T0& t0) const + { + return callWithArgumentTuple(makeTuple(t0)); + } + + /** + * \brief Call this object with two argument + * + * Shortcut for callWithArgumentTuple(makeTuple(t0,t1)). + */ + template<class T0, class T1> + Reference operator() (const T0& t0, const T1& t1) const + { + return callWithArgumentTuple(makeTuple(t0, t1)); + } + + /** + * \brief Call this object with three argument + * + * Shortcut for callWithArgumentTuple(makeTuple(t0,t1,t2)). + */ + template<class T0, class T1, class T2> + Reference operator() (const T0& t0, const T1& t1, const T2& t2) const + { + return callWithArgumentTuple(makeTuple(t0, t1, t2)); + } + + protected: + + /** + * \brief Assert that internal PyObject* is not NULL and callable and raise exception otherwise + * + * \param origin A string describing the origin of the error + */ + static void assertCallable(PyObject* p, const std::string& origin) + { + if (p and (not PyCallable_Check(p))) + DUNE_THROW(Dune::Exception, + "Python error occured." << std::endl << + " Origin: " << origin << std::endl << + " Error: Trying to use a non-callable as callable"); + } +}; + + +}; // end of namespace Python + + + +#else + #warning dunepython.hh was included but python was not found or enabled! +#endif // DUNE_FUFEM_PYTHON_CALLABLE_HH + + +#endif + -- GitLab