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