MUQ  0.4.3
PyAny.h
Go to the documentation of this file.
1 #ifndef PYANY_H_
2 #define PYANY_H_
3 
4 #include <boost/any.hpp>
5 
6 #include <typeindex>
7 
8 #include <cxxabi.h>
9 
10 #include "pybind11/pybind11.h"
11 #include "pybind11/eigen.h"
12 
13 namespace pybind11 { namespace detail {
14 
15  template <> struct type_caster<boost::any> {
16  public:
17 
18  PYBIND11_TYPE_CASTER(boost::any, _("boost::any"));
19 
20  bool load(handle src, bool) {
21 
22  PyObject *source = src.ptr();
23 
24  char thisStr[] = "this";
25 
26  // If the pyobject does not have a "this" attribute, then we should try to convert it to a native c++ type (e.g., double, int, string, etc...)
27  if (!PyObject_HasAttrString(source, thisStr))
28  {
29  std::string typeStr = source->ob_type->tp_name;
30  auto it = fromPythonMap.find(typeStr);
31  if( it != fromPythonMap.end()){
32  value = it->second(source);
33  return true;
34  }else{
35  value = boost::any(src.cast<pybind11::object>());
36  return true;
37  }
38  }
39 
40  // In this case, the python object is a class
41  PyObject* thisAttr = PyObject_GetAttrString(source, thisStr);
42  if (thisAttr == NULL)
43  {
44  std::cerr << "\nERROR: Could not get the this attr.\n\n";
45  }
46 
47  std::cerr << "ERROR: Don't have a good way to deal with generic classes yet...." << std::endl;
48  assert(false);
49 
50  Py_DECREF(thisAttr);
51  return false;
52  }
53 
54  static handle cast(boost::any src, return_value_policy /* policy */, handle /* parent */) {
55 
56  auto it = toPythonMap.find(src.type());
57  if(it != toPythonMap.end()){
58  return it->second(src);
59  }else{
60  std::cerr << "WARNING: Could not convert type " << demangle_typename(src.type().name()) << " directly to Python type. Trying to cast as pybind11::object." << std::endl;
61  return boost::any_cast<pybind11::object>(src);
62  }
63  }
64 
65  private:
66 
67  using EigenRowType = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
68  using EigenColType = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
69 
70  using EigenVectorType = Eigen::Matrix<double, Eigen::Dynamic,1>;
71  using EigenRowVectorType = Eigen::Matrix<double, 1, Eigen::Dynamic>;
72 
73  // A map of functions to convert a boost::any to a corresponding PyObject
74  static std::map<std::type_index, std::function<handle(boost::any const&)>> toPythonMap;
75  static std::map<std::string, std::function<boost::any(PyObject*)>> fromPythonMap;
76 
77  static std::map<std::type_index, std::function<handle(boost::any const&)>> createToPythonMap()
78  {
79  std::map<std::type_index, std::function<handle(boost::any const&)>> m;
80  m[typeid(long)] = [](boost::any const& x) { return PyLong_FromLong(boost::any_cast<long>(x));};
81  m[typeid(int)] = [](boost::any const& x) { return PyLong_FromLong(boost::any_cast<int>(x));};
82  m[typeid(unsigned)] = [](boost::any const& x) { return PyLong_FromLong(boost::any_cast<unsigned>(x));};
83  m[typeid(unsigned long)] = [](boost::any const& x) { return PyLong_FromLong(boost::any_cast<unsigned long>(x));};
84  m[typeid(double)] = [](boost::any const& x) { return PyFloat_FromDouble(boost::any_cast<double>(x));};
85  m[typeid(float)] = [](boost::any const& x) { return PyFloat_FromDouble(static_cast<double>(boost::any_cast<float>(x)));};
86  m[typeid(std::string)] = [](boost::any const& x) { return PyBytes_FromString(boost::any_cast<std::string>(x).c_str());};
87 
88  m[typeid(Eigen::Ref<EigenRowType>)] = [](boost::any const& x){
89  return eigen_array_cast<EigenProps<Eigen::Ref<EigenRowType>>>(boost::any_cast<Eigen::Ref<EigenRowType>>(x));
90  };
91 
92  m[typeid(Eigen::Ref<EigenColType>)] = [](boost::any const& x){
93  return eigen_array_cast<EigenProps<Eigen::Ref<EigenColType>>>(boost::any_cast<Eigen::Ref<EigenColType>>(x));
94  };
95 
96  m[typeid(EigenRowType)] = [](boost::any const& x){
97  return eigen_array_cast<EigenProps<EigenRowType>>(boost::any_cast<EigenRowType>(x));
98  };
99 
100  m[typeid(EigenColType)] = [](boost::any const& x){
101  return eigen_array_cast<EigenProps<EigenColType>>(boost::any_cast<EigenColType>(x));
102  };
103 
104  m[typeid(EigenVectorType)] = [](boost::any const& x){
105  return eigen_array_cast<EigenProps<EigenVectorType>>(boost::any_cast<EigenVectorType>(x));
106  };
107 
108  m[typeid(EigenRowVectorType)] = [](boost::any const& x){
109  return eigen_array_cast<EigenProps<EigenRowVectorType>>(boost::any_cast<EigenRowVectorType>(x));
110  };
111 
112 
113  return m;
114  }
115 
116  static std::map<std::string, std::function<boost::any(PyObject*)>> createFromPythonMap()
117  {
118  std::map<std::string, std::function<boost::any(PyObject*)>> m;
119  m["float"] = [](PyObject* obj) { return boost::any(PyFloat_AsDouble(obj));};
120  m["int"] = [](PyObject* obj) { return boost::any(PyLong_AsLong(obj));};
121  m["str"] = [](PyObject* obj){
122  PyObject* str_exc_type = PyObject_Repr(obj);
123 
124  PyObject* pyStr = PyUnicode_AsEncodedString(str_exc_type, "utf-8", "Error ~");
125  const char *strExcType = PyBytes_AS_STRING(pyStr);
126 
127  boost::any output = std::string(strExcType);
128 
129  Py_XDECREF(str_exc_type);
130  Py_XDECREF(pyStr);
131 
132  return output;
133  };
134 
135  m["numpy.ndarray"] = [](PyObject* obj){
136 
137  type_caster<Eigen::Ref<EigenRowType>> rowCaster;
138  bool res = rowCaster.load(obj, false);
139  if(res){
140  Eigen::Ref<EigenRowType> *ref = rowCaster;
141  return boost::any(*ref);
142  }else{
143  type_caster<Eigen::Ref<EigenColType>> colCaster;
144  bool res = colCaster.load(obj, false);
145 
146  if(!res){
147  std::cerr << "ERROR: Could not convert numpy array to Eigen::Ref. Current support is only for row-major and col-major arrays of doubles. Is the numpy array full of doubles?";
148  assert(res);
149  }
150 
151  Eigen::Ref<EigenColType> *ref = colCaster;
152  return boost::any(*ref);
153  }
154  };
155 
156  return m;
157  }
158 
159 
160 
161  static std::string demangle_typename(const char* name) {
162 
163  int status = -4; // some arbitrary value to eliminate the compiler warning
164 
165  // enable c++11 by passing the flag -std=c++11 to g++
166  std::unique_ptr<char, void(*)(void*)> res {
167  abi::__cxa_demangle(name, NULL, NULL, &status),
168  std::free
169  };
170 
171  return (status==0) ? res.get() : name ;
172  }
173  };
174 
175  // Fill in the map for functions that transform c++ types to python types
176  std::map<std::type_index, std::function<handle(boost::any const&)>>
177  type_caster<boost::any>::toPythonMap = type_caster<boost::any>::createToPythonMap();
178 
179  // Fill in the map for functions that transform python types to c++ types
180  std::map<std::string, std::function<boost::any(PyObject*)>>
181  type_caster<boost::any>::fromPythonMap = type_caster<boost::any>::createFromPythonMap();
182 
183 }} // namespace pybind11::detail
184 
185 #endif
std::string demangle_typename(const char *name)
Definition: FenicsPiece.h:28
void load(Archive &ar, boost::any &obj)
int int FloatType value
Definition: json.h:15223