Alexandria  2.22.0
Please provide a description of the project.
ExpressionTreeBuilder.icpp
Go to the documentation of this file.
1 /**
2  * @copyright (C) 2012-2020 Euclid Science Ground Segment
3  *
4  * This library is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU Lesser General Public License as published by the Free
6  * Software Foundation; either version 3.0 of the License, or (at your option)
7  * any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12  * details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "Pyston/Graph/PythonCall.h"
20 #include "Pyston/SharedContext.h"
21 
22 #ifdef PYSTON_EXPRESSIONTREEBUILDER_IMPL
23 
24 namespace Pyston {
25 
26 // Defined in Module.cpp
27 extern PyObject* pyRecoverableError;
28 
29 template <unsigned pos, typename... AN>
30 struct placeholderHelper;
31 
32 template <unsigned pos, typename A0, typename... AN>
33 struct placeholderHelper<pos, A0, AN...> {
34 
35  template <typename... Prototypes>
36  static void append(boost::python::list& placeholders, Prototypes&&... prototypes) {
37  placeholders.append(std::make_shared<Placeholder<A0>>(boost::python::len(placeholders)));
38  placeholderHelper<pos - 1, AN...>::append(placeholders, std::forward<Prototypes>(prototypes)...);
39  }
40 };
41 
42 template <unsigned pos, typename... AN>
43 struct placeholderHelper<pos, AttributeSet, AN...> {
44 
45  template <typename P1, typename... Prototypes>
46  static void append(boost::python::list& placeholders, const P1& p1, Prototypes&&... prototypes) {
47  placeholders.append(std::make_shared<Placeholder<AttributeSet>>(boost::python::len(placeholders), p1));
48  placeholderHelper<pos - 1, AN...>::append(placeholders, std::forward<Prototypes>(prototypes)...);
49  }
50 
51  template <typename... Prototypes>
52  static void append(boost::python::list&) {
53  static_assert(sizeof...(Prototypes) > 0, "AttributeSet on the signature but no prototype provided");
54  }
55 };
56 
57 template <>
58 struct placeholderHelper<0> {
59  static void append(boost::python::list&) {}
60 };
61 
62 template <typename R, typename... Args>
63 ExpressionTree<R(Args...)> ExpressionTreeBuilder::compiledOrWrapped(const boost::python::object& pyfunc,
64  const boost::python::list& placeholders) {
65  std::shared_ptr<Node<R>> root;
66  bool compiled;
67  std::shared_ptr<Exception> reason;
68 
69  // Try building a computing graph
70  try {
71  auto py_comp = pyfunc(*boost::python::tuple(placeholders));
72  root = boost::python::extract<std::shared_ptr<Node<R>>>(py_comp);
73  compiled = true;
74  }
75  catch (const boost::python::error_already_set&) {
76  // If the error is not recoverable, just bail out
77  if (!PyErr_ExceptionMatches(pyRecoverableError)) {
78  throw Exception();
79  }
80  // If it is recoverable (i.e. placeholder used on a control flow), wrap the call to python
81  reason = std::make_shared<Exception>();
82  PyErr_Clear();
83  root = std::make_shared<PythonCall<R>>(pyfunc);
84  compiled = false;
85  }
86  return ExpressionTree<R(Args...)>(compiled, root, std::move(reason));
87 }
88 
89 template <typename R, typename T>
90 ExpressionTree<R(const std::vector<T>&)>
91 ExpressionTreeBuilder::buildHelper<R(const std::vector<T>&)>::build(const boost::python::object& pyfunc, size_t n) {
92  GILLocker gil_ensure;
93  boost::python::list placeholders;
94  for (size_t i = 0; i < n; ++i) {
95  placeholders.append(boost::python::object(std::make_shared<Placeholder<T>>(i)));
96  }
97 
98  return compiledOrWrapped<R, const std::vector<T>&>(pyfunc, placeholders);
99 }
100 
101 template <typename R, typename... Args>
102 template <typename... Prototypes>
103 ExpressionTree<R(Args...)> ExpressionTreeBuilder::buildHelper<R(Args...)>::build(const boost::python::object& pyfunc,
104  Prototypes&&... prototypes) {
105  GILLocker gil_ensure;
106 
107  boost::python::list placeholders;
108  placeholderHelper<sizeof...(Args), typename std::remove_const<typename std::remove_reference<Args>::type>::type...>::
109  append(placeholders, std::forward<Prototypes>(prototypes)...);
110 
111  return compiledOrWrapped<R, Args...>(pyfunc, placeholders);
112 }
113 
114 template <typename Signature>
115 struct registerHelper;
116 
117 /**
118  * Register a function that does not receive a context
119  */
120 template <typename R, typename... Args>
121 struct registerHelper<R(Args...)> {
122  static void registerFunction(const std::string& repr, std::function<R(Args...)> functor) {
123  auto ns = boost::python::import("pyston");
124  // Add the function for Nodes
125  auto function = makeFunction<R(Args...)>(repr, functor);
126  boost::python::objects::add_to_namespace(ns, repr.c_str(), function);
127 
128  // Overload for the primitive types, so it can be evaluated even with non compilable expressions
129  auto direct =
130  boost::python::make_function(functor, boost::python::default_call_policies(), boost::mpl::vector<R, Args...>());
131  boost::python::objects::add_to_namespace(ns, repr.c_str(), direct);
132  }
133 };
134 
135 /**
136  * Register a function that receives a Context as first parameter
137  */
138 template <typename R, typename... Args>
139 struct registerHelper<R(const Context&, Args...)> {
140  static void registerFunction(const std::string& repr, std::function<R(const Context&, Args...)> functor) {
141  auto ns = boost::python::import("pyston");
142  // Add the function for Nodes
143  auto function = makeFunction<R(const Context&, Args...)>(repr, functor);
144  boost::python::objects::add_to_namespace(ns, repr.c_str(), function);
145 
146  // Overload for the primitive types, so it can be evaluated even with non compilable expressions
147  auto direct = boost::python::make_function(
148  [functor](Args... v) {
149  // Since this function needs a context, we pass along the global one
150  return functor(sharedContext, v...);
151  },
152  boost::python::default_call_policies(), boost::mpl::vector<R, Args...>());
153  boost::python::objects::add_to_namespace(ns, repr.c_str(), direct);
154  }
155 };
156 
157 template <typename Signature>
158 void ExpressionTreeBuilder::registerFunction(const std::string& repr, std::function<Signature> functor) {
159  registerHelper<Signature>::registerFunction(repr, functor);
160 }
161 
162 } // end of namespace Pyston
163 
164 #endif