Alexandria 2.31.0
SDC-CH common library for the Euclid project
Loading...
Searching...
No Matches
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
24namespace Pyston {
25
26// Defined in Module.cpp
27extern PyObject* pyRecoverableError;
28
29template <unsigned pos, typename... AN>
30struct placeholderHelper;
31
32template <unsigned pos, typename A0, typename... AN>
33struct 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
42template <unsigned pos, typename... AN>
43struct 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
57template <>
58struct placeholderHelper<0> {
59 static void append(boost::python::list&) {}
60};
61
62template <typename R, typename... Args>
63ExpressionTree<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
89template <typename R, typename T>
90ExpressionTree<R(const std::vector<T>&)>
91ExpressionTreeBuilder::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
101template <typename R, typename... Args>
102template <typename... Prototypes>
103ExpressionTree<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
114template <typename Signature>
115struct registerHelper;
116
117/**
118 * Register a function that does not receive a context
119 */
120template <typename R, typename... Args>
121struct 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 */
138template <typename R, typename... Args>
139struct 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
157template <typename Signature>
158void 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