k1a  1.1
Accelerated functionalities for k1lib
k1a::StrIter Class Reference

String iterator. More...

#include <StrIter.h>

Inheritance diagram for k1a::StrIter:
[legend]

Public Member Functions

 StrIter ()
 
 StrIter (PyObject *pyObj)
 
virtual std::string next ()
 
StrItertransform (std::vector< transformF > fs, bool decref=false)
 Transforms StrIter into other iterators. More...
 
long length ()
 
 ~StrIter ()
 

Static Public Member Functions

template<class T >
static PyObject * Py_next (PyObject *self)
 Default __next__ function. More...
 
template<class T >
static PyObject * Py_repr (PyObject *self)
 Default __repr__ function. More...
 
template<class T >
static Py_ssize_t Py_length (PyObject *self)
 
template<class T >
static void Py_dealloc (PyObject *self)
 Default dealloc function. More...
 
template<class T >
static PySequenceMethods * Py_as_sequence ()
 Default tp_as_sequence method generator. More...
 

Public Attributes

PyObject * pyObj
 Associated python object. More...
 
bool done
 Whether the iterator has any elements left. More...
 

Detailed Description

String iterator.

Basically just a base string iterator class.

For an overview of how memory is managed, check out this diagram:

A--ref--| B
val |--val

A, B, and C are Python objects that has an associated StrIter at A->val.

Let's assume that A is a string iterator that reads from a file, and B is a string iterator that adds some suffix text to every line of A. Then A owns A->val, B->val owns A, B owns B->val. Which means if B is deleted by Python, then it would trigger deletion of B->val, which would decrease Python's reference count of A, which can trigger deletion of A, which would trigger deletion of A->val.

Let's check out a chain with 3 StrIters:

A--ref--| B--ref--| C
val |--val |--val

Again, assume A is reading from a file, B is adding a suffix and C is adding a prefix. Let's also assume that only A and C are exposed to Python. For the user, the flow looks like this:

  • Creates A and A->val using Python
  • Runs a function that tries to create C
  • The function creates B and B->val
  • B->val obtains a reference to A. Now, A will not be deleted unless B->val is deleted
  • The function creates C and C->val
  • C->val obtains a reference to B. Now, B will not be deleted unless C->val is deleted
  • C is returned to the user

After this, reference count for al of them should be:

  • A: 2, from Python and from B->val
  • B: 1, from C->val
  • C: 1, from Python

Assuming the user no longer uses A and C, this is what would happen:

  • Python DECREF C
  • C counter goes to 0, thus Python deletes it
  • That triggers C->val deletion, which DECREF B
  • B counter goes to 0, thus Python deletes it
  • That triggers B->val deletion, which DECREF A
  • A counter is 1
  • Python DECREF A
  • A counter goes to 0, thus Python deletes it
  • That triggers A->val deletion

So the convention is, the StrIter objects should not be deleted outside of the associated Python object's dealloc method.

When subclassing this, you should implement StrIter::next. If there are no more elements, you should set StrIter::done to true, and return an empty string.

Definition at line 79 of file StrIter.h.

Constructor & Destructor Documentation

◆ StrIter() [1/2]

k1a::StrIter::StrIter ( )

Definition at line 14 of file StrIter.cpp.

14  {
15  done = false;
16  this->pyObj = NULL;
17 };
bool done
Whether the iterator has any elements left.
Definition: StrIter.h:82
PyObject * pyObj
Associated python object.
Definition: StrIter.h:81

◆ StrIter() [2/2]

k1a::StrIter::StrIter ( PyObject *  pyObj)

Definition at line 19 of file StrIter.cpp.

19  {
20  done = false;
21  this->pyObj = pyObj;
22 };

◆ ~StrIter()

k1a::StrIter::~StrIter ( )

Definition at line 66 of file StrIter.cpp.

66 {};

Member Function Documentation

◆ length()

long k1a::StrIter::length ( )

Definition at line 56 of file StrIter.cpp.

56  {
57  int count = 0;
58  while (true) {
59  next();
60  if (done) break;
61  count += 1;
62  }
63  return count;
64 }
virtual std::string next()
Definition: StrIter.cpp:51
Here is the call graph for this function:

◆ next()

std::string k1a::StrIter::next ( )
virtual

Reimplemented in k1a::StrIterInter, and k1a::StrIterCat.

Definition at line 51 of file StrIter.cpp.

51  {
52  if (debug) log_println("StrIter::next");
53  return "";
54 }
bool debug
Definition: utils.cpp:14
void log_println(T s)
Definition: utils.h:21
Here is the call graph for this function:

◆ Py_as_sequence()

template<class T >
PySequenceMethods * k1a::StrIter::Py_as_sequence
static

Default tp_as_sequence method generator.

Example:

PyTypeObject PyStrIterCat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) "StrIterCat",
StrIter::Py_as_sequence<PyStrIterCat>(),
}
PyTypeObject PyStrIterCat_Type
Definition: StrIterCat.cpp:64
Template Parameters
T
Returns
PySequenceMethods*

Definition at line 118 of file StrIter.h.

118  {
119  PySequenceMethods *answer = new PySequenceMethods();
120  *answer = {
121  (lenfunc)StrIter::Py_length<T>, /* sq_length */
122  0, /* sq_concat */
123  0, /* sq_repeat */
124  0, /* sq_item */
125  0, /* sq_slice */
126  0, /* sq_ass_item */
127  0, /* sq_ass_slice */
128  0, /* sq_contains */
129  0, /* sq_inplace_concat */
130  0, /* sq_inplace_repeat */
131  };
132  return answer;
133 }

◆ Py_dealloc()

template<class T >
void k1a::StrIter::Py_dealloc ( PyObject *  self)
static

Default dealloc function.

Example:

PyTypeObject PyStrIterCat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) "StrIterCat",
(destructor)StrIter::Py_dealloc<PyStrIterCat>,
}
Template Parameters
T
Parameters
self
Returns
PyObject*

Definition at line 202 of file StrIter.h.

202  {
203  if (debug) log_println(std::string("StrIter::Py_dealloc - ") + demangle(typeid(T).name()));
204  T *_self = (T *)self;
205  delete _self->val;
206  Py_TYPE(_self)->tp_free(self);
207 }
std::string demangle(const char *name)
Demangles C++ signatures.
Definition: utils.cpp:39
Here is the call graph for this function:

◆ Py_length()

template<class T >
Py_ssize_t k1a::StrIter::Py_length ( PyObject *  self)
static

Definition at line 182 of file StrIter.h.

182  {
183  return ((T *)self)->val->length();
184 }

◆ Py_next()

template<class T >
PyObject * k1a::StrIter::Py_next ( PyObject *  self)
static

Default __next__ function.

Example:

PyTypeObject PyStrIterCat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) "StrIterCat",
(iternextfunc)StrIter::Py_next<PyStrIterCat>,
}
Template Parameters
T
Parameters
self
Returns
PyObject*

Definition at line 151 of file StrIter.h.

151  {
152  T *it = (T *)self;
153  if (debug) log_println(std::string("StrIter::Py_next - ") + demangle(typeid(T).name()));
154  std::string res = it->val->next();
155  if (it->val->done)
156  return NULL;
157  else
158  return PyUnicode_FromString(res.c_str());
159 }
Here is the call graph for this function:

◆ Py_repr()

template<class T >
PyObject * k1a::StrIter::Py_repr ( PyObject *  self)
static

Default __repr__ function.

Example:

PyTypeObject PyStrIterCat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) "StrIterCat",
(reprfunc)StrIter::Py_next<PyStrIterCat>,
}
Template Parameters
T
Parameters
self
Returns
PyObject*

Definition at line 177 of file StrIter.h.

177  {
178  return PyUnicode_FromString(std::string("<").append(demangle(typeid(T).name())).append(" object>").c_str());
179 }
Here is the call graph for this function:

◆ transform()

StrIter * k1a::StrIter::transform ( std::vector< transformF fs,
bool  decref = false 
)

Transforms StrIter into other iterators.

This passes ownership of the associated Python object to you. However, you don't own intermediary StrIter objects as those are cleaned automatically for you.

If this StrIter is internal only, and is not returned to Python, then you can set decref to true, just for convenience.

Parameters
fsVector of transform functions
decrefWhether to take ownership of this StrIter
Returns
StrIter*

Definition at line 38 of file StrIter.cpp.

38  {
39  if (debug) log_println("StrIter::transform");
40  StrIter *answer = this;
41  PyObject *lastPyObj = NULL;
42  for (auto f : fs) {
43  answer = ((PyStrIterInter *)PyStrIterInter_new(answer, f))->val;
44  Py_XDECREF(lastPyObj);
45  lastPyObj = answer->pyObj;
46  }
47  if (decref) Py_XDECREF(pyObj);
48  return answer;
49 }
PyObject * PyStrIterInter_new(StrIter *og, transformF f)
Here is the call graph for this function:

Member Data Documentation

◆ done

bool k1a::StrIter::done

Whether the iterator has any elements left.

Definition at line 82 of file StrIter.h.

◆ pyObj

PyObject* k1a::StrIter::pyObj

Associated python object.

Definition at line 81 of file StrIter.h.


The documentation for this class was generated from the following files: