2 * @copyright (C) 2012-2020 Euclid Science Ground Segment
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)
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
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
19#include "Pyston/Graph/PythonCall.h"
20#include "Pyston/SharedContext.h"
22#ifdef PYSTON_EXPRESSIONTREEBUILDER_IMPL
26// Defined in Module.cpp
27extern PyObject* pyRecoverableError;
29template <unsigned pos, typename... AN>
30struct placeholderHelper;
32template <unsigned pos, typename A0, typename... AN>
33struct placeholderHelper<pos, A0, AN...> {
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)...);
42template <unsigned pos, typename... AN>
43struct placeholderHelper<pos, AttributeSet, AN...> {
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)...);
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");
58struct placeholderHelper<0> {
59 static void append(boost::python::list&) {}
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;
67 std::shared_ptr<Exception> reason;
69 // Try building a computing graph
71 auto py_comp = pyfunc(*boost::python::tuple(placeholders));
72 root = boost::python::extract<std::shared_ptr<Node<R>>>(py_comp);
75 catch (const boost::python::error_already_set&) {
76 // If the error is not recoverable, just bail out
77 if (!PyErr_ExceptionMatches(pyRecoverableError)) {
80 // If it is recoverable (i.e. placeholder used on a control flow), wrap the call to python
81 reason = std::make_shared<Exception>();
83 root = std::make_shared<PythonCall<R>>(pyfunc);
86 return ExpressionTree<R(Args...)>(compiled, root, std::move(reason));
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) {
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)));
98 return compiledOrWrapped<R, const std::vector<T>&>(pyfunc, placeholders);
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;
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)...);
111 return compiledOrWrapped<R, Args...>(pyfunc, placeholders);
114template <typename Signature>
115struct registerHelper;
118 * Register a function that does not receive a context
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);
128 // Overload for the primitive types, so it can be evaluated even with non compilable expressions
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);
136 * Register a function that receives a Context as first parameter
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);
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...);
152 boost::python::default_call_policies(), boost::mpl::vector<R, Args...>());
153 boost::python::objects::add_to_namespace(ns, repr.c_str(), direct);
157template <typename Signature>
158void ExpressionTreeBuilder::registerFunction(const std::string& repr, std::function<Signature> functor) {
159 registerHelper<Signature>::registerFunction(repr, functor);
162} // end of namespace Pyston