Dune-Fufem 2.11-git
Loading...
Searching...
No Matches
callable.hh
Go to the documentation of this file.
1// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set ts=8 sw=4 et sts=4:
3
4// SPDX-FileCopyrightText: Copyright © DUNE-FUFEM Project contributors, see file AUTHORS.md
5// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later
6
7#ifndef DUNE_FUFEM_PYTHON_CALLABLE_HH
8#define DUNE_FUFEM_PYTHON_CALLABLE_HH
9
10// Only introduce the dune-python interface if python
11// was found and enabled.
12#if HAVE_PYTHON || DOXYGEN
13
14#include <Python.h>
15
16#include <string>
17#include <sstream>
18#include <vector>
19#include <map>
20
27
28#include <dune/grid/common/gridfactory.hh>
29
31
32
33namespace Python
34{
35
36// forward declarations
37void handlePythonError(const std::string& origin, const std::string& message);
39
40namespace Imp
41{
42 // forward declarations
43 PyObject* inc(PyObject* p);
44
45
46
47 // Helper for marking a keyword argument
48 template<class Key, class Value>
49 struct KeyWordArgument
50 {
51 Key key;
52 Value value;
53 };
54
55
56
57 // Check for KeyWordArgument
58 template<class T>
59 struct IsKeyWordArgument : std::false_type {};
60
61 template<class Key, class Value>
62 struct IsKeyWordArgument<KeyWordArgument<Key, Value>> : std::true_type {};
63
64 // Count positional (non-keyword) arguments up to pos-1.
65 // The result is returned as compile time constant using index_constant.
66 template<std::size_t pos, class... Args>
67 auto positionalArgumentCount()
68 {
69 using ArgTuple = std::tuple<Args...>;
70 return Dune::unpackIntegerSequence([] (auto... i) {
71 return Dune::index_constant<(0 + ... + not(Imp::IsKeyWordArgument<std::decay_t<std::tuple_element_t<i, ArgTuple>>>::value))>();
73 }
74
75 // Helper for tagging string as keyword
76 template<class Char>
77 struct KeyWord {
78 std::string key;
79 template <typename Value>
80 KeyWordArgument<std::string, Value> operator=(Value&& value) const {
81 return {key, std::forward<Value>(value)};
82 }
83 };
84
85 // Parse argument list. The list may contain positional and keyword arguments.
86 // The parameters to this function are two callback functions, followed by
87 // the arguments to be parsed. Positional and keyword arguments may be interleaved.
88 // In any case only positional arguments are counted when determining the position
89 // of a positional argument.
90 //
91 // The first callback will be called with (pos,arg) for each positional argument arg
92 // with its position pos (as compile time value using Dune::index_constant).
93 // The second callback will be called with (keyword, value) for each
94 // keyword argument.
95 template<class PositionalCallback, class KeyWordCallBack, class... Args>
96 void parseKeywordArguments(PositionalCallback&& posCallback, KeyWordCallBack&& kwCallBack, Args&&... args)
97 {
98 auto processArgument = [&](auto i, auto&& arg)
99 {
100 if constexpr (IsKeyWordArgument<std::decay_t<decltype(arg)>>::value)
101 kwCallBack(arg.key, arg.value);
102 else
103 posCallback(positionalArgumentCount<i, Args...>(), std::forward<decltype(arg)>(arg));
104 };
105 // Unfold argument pack to process arguments. Doing this via
106 // std::initializer_list guarantees the proper evaluation order.
107 // To additionally pass the argument indices as variadic argument pack,
108 // we use unpackIntegerSequence.
109 //
110 // Here one would like to simply unpack args like this:
111 // std::initializer_list<int>{ (processArgument(i, args), 0)...};
112 // But capturing parameter packs does not work in C++17
113 // (in fact gcc allows it). Hence we have to artificially
114 // wrap in this in a tuple and access entries using std::get.
115 //
116 // Create a tuple preserving value categories.
117 // This will store appropriate l- and r-value references
118 // to the individual arguments.
119 auto argTuple = std::forward_as_tuple(std::forward<Args>(args)...);
120 Dune::unpackIntegerSequence([&](auto... i) {
121 // Extract tuple entries restoring the value category.
122 // Notice that due to reference collapsing this also returns
123 // l-value references properly. Furthermore doing multiple
124 // move's on the same tuple is OK here, because each move
125 // only changes the value category of the returned entry
126 // but does not modify the tuple itself.
127 [[maybe_unused]] auto dummy = std::initializer_list<int>{ (processArgument(i, std::get<i>(std::move(argTuple))), 0)...};
128 }, std::index_sequence_for<Args...>());
129 }
130
131} // namespace Imp
132
133
146template<class Key, class Value>
147auto arg(Key key, Value&& value)
148{
150 return Imp::KeyWordArgument<StoredKey, Value>{key, std::forward<Value>(value)};
151}
152
153namespace Literals {
159 Imp::KeyWord<char> operator ""_a(const char* keyword, std::size_t) {
160 return {keyword};
161 }
162}
163
164
165
172class Callable :
173 public Reference
174{
175 public:
176
181 Reference()
182 {}
183
209 Callable(PyObject* p) :
210 Reference(p)
211 {
212 assertCallable(p_, "Callable(PyObject*)");
213 }
214
226 Callable(const Reference& other) :
227 Reference(other)
228 {
229 assertCallable(p_, "Callable(Reference&)");
230 }
231
235 virtual ~Callable()
236 {}
237
244 virtual Callable& operator= (const Reference& other)
245 {
246 assertCallable(other, "Callable::operator=(Reference&)");
248 return *this;
249 }
250
269 {
270 assertPyObject("Callable::callWithArgumentTuple()");
271 PyObject* result = PyObject_CallObject(p_, args);
272 if (not result)
273 handlePythonError("Callable::callWithArgumentTuple()", "failed to call object");
274 return result;
275 }
276
294 {
295 assertPyObject("Callable::callWithArgumentTupleAndKeywordArgs()");
296 PyObject* result = PyObject_Call(p_, args, keywordArgs);
297 if (not result)
298 handlePythonError("Callable::callWithArgumentTupleAndKeywordArgs()", "failed to call object");
299 return result;
300 }
301
313 template<class... Args>
314 Reference operator() (const Args&... args) const
315 {
316 static constexpr std::size_t kwArgCount = (0 + ... + Imp::IsKeyWordArgument<Args>::value);
317 if constexpr (kwArgCount == 0)
318 return callWithArgumentTuple(makeTuple(args...));
319 else
320 {
321 Reference kwArgDict = dict();
322 Reference argTuple = PyTuple_New(sizeof...(Args)-kwArgCount);
323 Imp::parseKeywordArguments(
324 [&](auto position, auto&& arg) {
325 PyTuple_SetItem(argTuple, position, Imp::inc(makeObject(arg)));
326 }, [&](auto keyword, auto&& value) {
327 PyDict_SetItem(kwArgDict, makeObject(keyword), makeObject(value));
328 // We have to use inc() since PyTuple_SetItem STEALS a reference!
329 }, args...);
330 return callWithArgumentTupleAndKeywordArgs(argTuple, kwArgDict);
331 }
332 }
333
334 protected:
335
342 static void assertCallable(PyObject* p, const std::string& origin)
343 {
344 if (p and (not PyCallable_Check(p)))
346 "Python error occurred." << std::endl <<
347 " Origin: " << origin << std::endl <<
348 " Error: Trying to use a non-callable as callable");
349 }
350};
351
352
365template<class ResultType>
366auto make_function(Callable pyCallable)
367{
368 return [pyCallable](auto&&... args) {
369 return pyCallable(std::forward<decltype(args)>(args)...).template toC<ResultType>();
370 };
371}
372
373} // end of namespace Python
374
375
376
377#else
378 #warning dunepython.hh was included but python was not found or enabled!
379#endif // DUNE_FUFEM_PYTHON_CALLABLE_HH
380
381
382#endif
383
SLList< T, A > & operator=(const SLList< T, A > &other)
decltype(auto) constexpr unpackIntegerSequence(F &&f, std::integer_sequence< I, i... > sequence)
void message(const std::string &msg)
virtual void operator()()=0
#define DUNE_THROW(E,...)
Definition callable.hh:34
auto make_function(Callable pyCallable)
Convert Python::Callable to C-function object.
Definition callable.hh:366
void handlePythonError(const std::string &origin, const std::string &message)
If a python error occurred throw an exception and clear python error indicator.
Definition common.hh:589
auto arg(Key key, Value &&value)
Create a keyword argument.
Definition callable.hh:147
Reference makeTuple(const Ts &... ts)
create a Python tuple
Definition common.hh:364
Reference makeObject(const T &t)
Create python object from C++ object.
Definition common.hh:351
Reference dict()
Create an empty Python tuple.
Definition common.hh:380
Wrapper class for callable python objects.
Definition callable.hh:174
Callable(const Reference &other)
Construct Callable from Reference.
Definition callable.hh:226
Reference callWithArgumentTupleAndKeywordArgs(const Reference &args, const Reference &keywordArgs) const
Call this Reference with positional arguments given as tuple and keyword arguments given as dictionar...
Definition callable.hh:293
static void assertCallable(PyObject *p, const std::string &origin)
Assert that PyObject* is not NULL and callable and raise exception otherwise.
Definition callable.hh:342
virtual Callable & operator=(const Reference &other)
Assignment.
Definition callable.hh:244
virtual ~Callable()
Destructor.
Definition callable.hh:235
Callable()
Construct empty Callable.
Definition callable.hh:180
Reference callWithArgumentTuple(const Reference &args) const
Call this Reference with arguments given as tuple.
Definition callable.hh:268
Callable(PyObject *p)
Construct Callable from PyObject*.
Definition callable.hh:209
Wrapper class for python objects.
Definition reference.hh:77
virtual Reference & operator=(const Reference &other)
Assignment.
Definition reference.hh:145
PyObject * p_
Definition reference.hh:299
void assertPyObject(const std::string &origin) const
Assert that internal PyObject* is not NULL and raise exception otherwise.
Definition reference.hh:288
T endl(T... args)
T forward_as_tuple(T... args)
T forward(T... args)