| 1 |
/* |
|---|
| 2 |
Copyright 2006, 2007 Kirk McDonald |
|---|
| 3 |
|
|---|
| 4 |
Permission is hereby granted, free of charge, to any person obtaining a copy of |
|---|
| 5 |
this software and associated documentation files (the "Software"), to deal in |
|---|
| 6 |
the Software without restriction, including without limitation the rights to |
|---|
| 7 |
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
|---|
| 8 |
of the Software, and to permit persons to whom the Software is furnished to do |
|---|
| 9 |
so, subject to the following conditions: |
|---|
| 10 |
|
|---|
| 11 |
The above copyright notice and this permission notice shall be included in all |
|---|
| 12 |
copies or substantial portions of the Software. |
|---|
| 13 |
|
|---|
| 14 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|---|
| 15 |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|---|
| 16 |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|---|
| 17 |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|---|
| 18 |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|---|
| 19 |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|---|
| 20 |
SOFTWARE. |
|---|
| 21 |
*/ |
|---|
| 22 |
|
|---|
| 23 |
/** |
|---|
| 24 |
* This module provides the support for wrapping opApply with Python's |
|---|
| 25 |
* iteration interface using Mikola Lysenko's StackThreads package. |
|---|
| 26 |
*/ |
|---|
| 27 |
module pyd.iteration; |
|---|
| 28 |
|
|---|
| 29 |
import python; |
|---|
| 30 |
|
|---|
| 31 |
import pyd.class_wrap; |
|---|
| 32 |
import pyd.dg_convert; |
|---|
| 33 |
import pyd.exception; |
|---|
| 34 |
import pyd.make_object; |
|---|
| 35 |
|
|---|
| 36 |
import std.traits; |
|---|
| 37 |
|
|---|
| 38 |
import st.stackcontext; |
|---|
| 39 |
|
|---|
| 40 |
// This exception is for yielding a PyObject* from within a StackContext. |
|---|
| 41 |
class PydYield : Exception { |
|---|
| 42 |
PyObject* m_py; |
|---|
| 43 |
this(PyObject* py) { |
|---|
| 44 |
super(""); |
|---|
| 45 |
m_py = py; |
|---|
| 46 |
} |
|---|
| 47 |
PyObject* item() { return m_py; } |
|---|
| 48 |
} |
|---|
| 49 |
|
|---|
| 50 |
// Creates an iterator object from an object. |
|---|
| 51 |
PyObject* PydStackContext_FromWrapped(T, alias Iter = T.opApply, iter_t = typeof(&Iter)) (T obj) { |
|---|
| 52 |
// Get the number of args the opApply's delegate argument takes |
|---|
| 53 |
alias ParameterTypeTuple!(iter_t) IInfo; |
|---|
| 54 |
alias ParameterTypeTuple!(IInfo[0]) Info; |
|---|
| 55 |
const uint ARGS = Info.length; |
|---|
| 56 |
auto sc = new StackContext(delegate void() { |
|---|
| 57 |
T o = obj; |
|---|
| 58 |
fn_to_dg!(iter_t) t = dg_wrapper!(T, iter_t)(o, &Iter); |
|---|
| 59 |
// We yield so we can be sure to get the local variables in the |
|---|
| 60 |
// enclosing function's stack frame. |
|---|
| 61 |
StackContext.yield(); |
|---|
| 62 |
|
|---|
| 63 |
t(delegate int(inout Info i) { |
|---|
| 64 |
StackContext.throwYield(new PydYield(PyTuple_FromItems(i))); |
|---|
| 65 |
return 0; |
|---|
| 66 |
}); |
|---|
| 67 |
}); |
|---|
| 68 |
// Initialize the StackContext |
|---|
| 69 |
sc.run(); |
|---|
| 70 |
return WrapPyObject_FromObject(sc); |
|---|
| 71 |
} |
|---|
| 72 |
|
|---|
| 73 |
template wrapped_iter(T, alias Iter, iter_t = typeof(&Iter)) { |
|---|
| 74 |
alias wrapped_class_object!(T) wrap_object; |
|---|
| 75 |
|
|---|
| 76 |
// Returns an iterator object for this class |
|---|
| 77 |
extern (C) |
|---|
| 78 |
PyObject* iter(PyObject* _self) { |
|---|
| 79 |
return exception_catcher({ |
|---|
| 80 |
wrap_object* self = cast(wrap_object*)_self; |
|---|
| 81 |
|
|---|
| 82 |
return PydStackContext_FromWrapped!(T, Iter, iter_t)(self.d_obj); |
|---|
| 83 |
}); |
|---|
| 84 |
} |
|---|
| 85 |
} |
|---|
| 86 |
|
|---|
| 87 |
// Advances an iterator object |
|---|
| 88 |
extern (C) |
|---|
| 89 |
PyObject* sc_iternext(PyObject* _self) { |
|---|
| 90 |
return exception_catcher(delegate PyObject*() { |
|---|
| 91 |
alias wrapped_class_object!(StackContext) PydSC_object; |
|---|
| 92 |
PydSC_object* self = cast(PydSC_object*)_self; |
|---|
| 93 |
|
|---|
| 94 |
try { |
|---|
| 95 |
// If the StackContext is done, cease iteration. |
|---|
| 96 |
if (!self.d_obj.ready()) { |
|---|
| 97 |
return null; |
|---|
| 98 |
} |
|---|
| 99 |
self.d_obj.run(); |
|---|
| 100 |
} |
|---|
| 101 |
// The StackContext class yields values by throwing an exception. |
|---|
| 102 |
// We catch it and pass the converted value into Python. |
|---|
| 103 |
catch (PydYield y) { |
|---|
| 104 |
return y.item(); |
|---|
| 105 |
} |
|---|
| 106 |
return null; |
|---|
| 107 |
}); |
|---|
| 108 |
} |
|---|
| 109 |
|
|---|
| 110 |
/// Readies the iterator class if it hasn't been already. |
|---|
| 111 |
void PydStackContext_Ready() { |
|---|
| 112 |
alias wrapped_class_type!(StackContext) type; |
|---|
| 113 |
alias wrapped_class_object!(StackContext) PydSC_object; |
|---|
| 114 |
|
|---|
| 115 |
if (!is_wrapped!(StackContext)) { |
|---|
| 116 |
type.ob_type = PyType_Type_p; |
|---|
| 117 |
type.tp_basicsize = PydSC_object.sizeof; |
|---|
| 118 |
type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; |
|---|
| 119 |
type.tp_name = "PydOpApplyWrapper"; |
|---|
| 120 |
|
|---|
| 121 |
type.tp_iter = &PyObject_SelfIter; |
|---|
| 122 |
type.tp_iternext = &sc_iternext; |
|---|
| 123 |
|
|---|
| 124 |
PyType_Ready(&type); |
|---|
| 125 |
is_wrapped!(StackContext) = true; |
|---|
| 126 |
//wrapped_classes[typeid(StackContext)] = true; |
|---|
| 127 |
} |
|---|
| 128 |
} |
|---|