Cantera  2.3.0
funcWrapper.h
1 // This file is part of Cantera. See License.txt in the top-level directory or
2 // at http://www.cantera.org/license.txt for license and copyright information.
3 
4 #ifndef CT_CYTHON_FUNC_WRAPPER
5 #define CT_CYTHON_FUNC_WRAPPER
6 
9 #include <stdexcept>
10 
11 #define CANTERA_USE_INTERNAL
12 #include "cantera/clib/clib_defs.h"
13 #include "Python.h"
14 
15 typedef double(*callback_wrapper)(double, void*, void**);
16 
17 // A C++ exception that holds a Python exception so that it can be re-raised
18 // by translate_exception()
19 class CallbackError : public Cantera::CanteraError
20 {
21 public:
22  CallbackError(void* type, void* value) :
23  m_type((PyObject*) type),
24  m_value((PyObject*) value)
25  {
26  }
27  const char* what() const throw() {
28  formattedMessage_ = "\n" + std::string(71, '*') + "\n";
29  formattedMessage_ += "Exception raised in Python callback function:\n";
30 
31  PyObject* name = PyObject_GetAttrString(m_type, "__name__");
32  PyObject* value_str = PyObject_Str(m_value);
33 
34  #if PY_MAJOR_VERSION > 2
35  PyObject* name_bytes = PyUnicode_AsASCIIString(name);
36  PyObject* value_bytes = PyUnicode_AsASCIIString(value_str);
37  #else
38  PyObject* name_bytes = PyObject_Bytes(name);
39  PyObject* value_bytes = PyObject_Bytes(value_str);
40  #endif
41 
42  if (name_bytes) {
43  formattedMessage_ += PyBytes_AsString(name_bytes);
44  Py_DECREF(name_bytes);
45  } else {
46  formattedMessage_ += "<error determining exception type>";
47  }
48 
49  formattedMessage_ += ": ";
50 
51  if (value_bytes) {
52  formattedMessage_ += PyBytes_AsString(value_bytes);
53  Py_DECREF(value_bytes);
54  } else {
55  formattedMessage_ += "<error determining exception message>";
56  }
57 
58  Py_XDECREF(name);
59  Py_XDECREF(value_str);
60 
61  formattedMessage_ += "\n" + std::string(71, '*') + "\n";
62  return formattedMessage_.c_str();
63  }
64 
65  PyObject* m_type;
66  PyObject* m_value;
67 };
68 
69 
70 // A function of one variable implemented as a callable Python object
71 class Func1Py : public Cantera::Func1
72 {
73 public:
74  Func1Py(callback_wrapper callback, void* pyobj) :
75  m_callback(callback),
76  m_pyobj(pyobj) {
77  }
78 
79  double eval(double t) const {
80  void* err[2] = {0, 0};
81  double y = m_callback(t, m_pyobj, err);
82  if (err[0]) {
83  throw CallbackError(err[0], err[1]);
84  }
85  return y;
86  }
87 
88 private:
89  callback_wrapper m_callback;
90  void* m_pyobj;
91 };
92 
93 extern "C" {
94  CANTERA_CAPI PyObject* pyCanteraError;
95 }
96 
97 // Translate C++ Exceptions generated by Cantera to appropriate Python
98 // exceptions. Used with Cython function declarations, e.g:
99 // cdef double eval(double) except +translate_exception
100 inline int translate_exception()
101 {
102  try {
103  if (!PyErr_Occurred()) {
104  // Let the latest Python exception pass through and ignore the
105  // current one.
106  throw;
107  }
108  } catch (const CallbackError& exn) {
109  // Re-raise a Python exception generated in a callback
110  PyErr_SetObject(exn.m_type, exn.m_value);
111  } catch (const std::out_of_range& exn) {
112  PyErr_SetString(PyExc_IndexError, exn.what());
113  } catch (const Cantera::CanteraError& exn) {
114  PyErr_SetString(pyCanteraError, exn.what());
115  } catch (const std::exception& exn) {
116  PyErr_SetString(PyExc_RuntimeError, exn.what());
117  } catch (...) {
118  PyErr_SetString(PyExc_Exception, "Unknown exception");
119  }
120  return 0;
121 }
122 
123 #endif
std::string formattedMessage_
Formatted message returned by what()
Definition: ctexceptions.h:119
const char * what() const
Get a description of the error.
virtual doublereal eval(doublereal t) const
Evaluate the function.
Definition: Func1.cpp:60
Base class for &#39;functor&#39; classes that evaluate a function of one variable.
Definition: Func1.h:41
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...