How to make an iterable class in C++? - python

I have a linked list code:
#include <iostream>
class Node
{
private:
/* data */
public:
int value;
Node* next;
Node(int value){
this->value = value;
this->next = nullptr;
}
};
class LinkedList
{
private:
/* data */
public:
Node *start;
LinkedList(){
this->start = nullptr;
}
void insert(int value){
Node *node = new Node(value);
if (this->start == nullptr)
{
this->start = node;
}else
{
Node* temp = this->start;
while (temp->next != nullptr)
{
temp = temp->next;
}
temp->next = node;
}
}
void print(){
Node* temp = this->start;
while (temp != nullptr)
{
std::cout<<temp->value<<std::endl;
temp = temp->next;
}
}
void __iter__(){
Node* node = this->start;
while (node)
{
yield node;
node = node->next;
}
}
};
int main(int argc, char const *argv[])
{
LinkedList listed;
listed.insert(4);
listed.insert(7);
listed.insert(9);
listed.insert(6);
listed.print();
return 0;
}
As you can see I have __iter__ method in my LinkedList class, but I have found that yield is not define to use in C++, so my console just shows:
info.cpp: In member function 'void LinkedList::__iter__()':
info.cpp:59:13: error: 'yield' was not declared in this scope
59 | yield node;
| ^~~~~
I am taking as reference the next piece of python code:
def __iter__(self):
node = self.start
while node:
yield node
node = node.next
I hope you can help me to solve that, thanks.

Take a look at this source: https://en.cppreference.com/w/cpp/iterator/iterator
It says that
std::iterator is the base class provided to simplify definitions of the required types for iterators.
Furthermore:
This source https://cplusplus.com/reference/iterator/iterator/
gives you the following example:
// std::iterator example
#include <iostream> // std::cout
#include <iterator> // std::iterator, std::input_iterator_tag
class MyIterator : public std::iterator<std::input_iterator_tag, int>
{
int* p;
public:
MyIterator(int* x) :p(x) {}
MyIterator(const MyIterator& mit) : p(mit.p) {}
MyIterator& operator++() {++p;return *this;}
MyIterator operator++(int) {MyIterator tmp(*this); operator++(); return tmp;}
bool operator==(const MyIterator& rhs) const {return p==rhs.p;}
bool operator!=(const MyIterator& rhs) const {return p!=rhs.p;}
int& operator*() {return *p;}
};
int main () {
int numbers[]={10,20,30,40,50};
MyIterator from(numbers);
MyIterator until(numbers+5);
for (MyIterator it=from; it!=until; it++)
std::cout << *it << ' ';
std::cout << '\n';
return 0;
}
It includes the iterator via #include <iterator>, defines its operations as public and then inside the main function effectively uses it. You will need to apply something similar, but you will need to step between pointers for your operator++ methods.

Related

Use closure in PyGetSetDef for reusing attribute getter-settter

As the title suggests, I'm trying to create a bunch of attributes but the code is getting repetitive and messy. I want to use the closure argument to make the code more compact.
According to the C API reference, the closure is a function pointer that provides additional information for getters/setters. I have not been able to find an example of it in use.
This is how I am currently using it:
static void closure_1() {};
static void closure_2() {};
...
static PyObject *
FOO_getter(FOO* self, void *closure) {
if (closure == &closure_1) {
return self->bar_1;
} else if (closure == &closure_2) {
return self->bar_2;
}
}
static int
FOO_setter(FOO* self, PyObject *value, void *closure) {
if (closure == &closure_1) {
if (somehow value is invalid) {
PyErr_SetString(PyExc_ValueError, "invalid value for bar_1.");
return -1;
}
} else if (closure == closure_2) {
if (somehow value is invalid) {
PyErr_SetString(PyExc_ValueError, "invalid value for bar_2.");
return -1;
}
}
return 0;
}
static PyGetSetDef FOO_getsetters[] = {
{"bar_1", (getter) FOO_getter, (setter) FOO_setter, "bar_1 attribute", closure_1},
{"bar_2", (getter) FOO_getter, (setter) FOO_setter, "bar_2 attribute", closure_2},
{NULL} /* Sentinel */
};
...
It works the way I want it to, but it looks more like a hack than something "pythonic". Is there a better way to handle this? e.g. call the closure in some way.
I guess this "closure" is used to pass an extra context to Foo_getter. It should be something that simplifies accessing members of Foo. Documentation it likely wrong. It should be "optional pointer", not "optional function pointer".
Consider passing offsets of the members. Offsets to struct members can be easily obtained with standard offsetof macro defined in stddef.h. It is a small unsigned integer that will fit to void* type.
static PyGetSetDef FOO_getsetters[] = {
{"bar_1", (getter) FOO_getter, (setter) FOO_setter, "bar_1 attribute", (void*)offsetof(FOO, bar_1)},
{"bar_2", (getter) FOO_getter, (setter) FOO_setter, "bar_2 attribute", (void*)offsetof(FOO, bar_2)},
{NULL} /* Sentinel */
};
Now the getter could be:
static PyObject *
FOO_getter(FOO* self, void *closure) {
// pointer to location where the FOO's member is stored
char *memb_ptr = (char*)self + (size_t)closure;
// cast to `PyObject**` because `mem_ptr` points to location where a pointer to `PyObject` is stored
return *(PyObject**)mem_ptr;
}
Use similar schema for the setter.
Despite the documentation, I'm assuming the closure can be any pointer you want. So how about passing an "object", seeing as C doesn't support closures (short of literally generating functions at run-time).
In an object, we can store the offset of the member in FOO, and a pointer to an attribute-specific validator.
typedef int (*Validator)(FOO *, const struct Attribute *, void *);
typedef struct Attribute {
const char *name;
size_t offset;
Validator validator;
} Attribute;
static PyObject **resolve_offset(FOO *self, const Attribute *attr) {
return (PyObject **)( ( (char *)self ) + attr->offset );
}
static PyObject *FOO_getter(FOO *self, void *_attr) {
const Attribute *attr = (const Attribute *)_attr;
return *resolve_offset(self, attr);
}
static int FOO_setter(FOO *self, PyObject *val, void *_attr) {
const Attribute *attr = (const Attribute *)_attr;
if (attr->validator(self, attr, val)) {
*resolve_offset(self, attr) = val;
return 0;
} else {
// Building the string to include attr->name is left to you.
PyErr_SetString(PyExc_ValueError, "invalid value.");
return -1;
}
}
static int FOO_bar_1_validator(FOO *self, const Attribute *attr, void *val) { ... }
static int FOO_bar_2_validator(FOO *self, const Attribute *attr, void *val) { ... }
#define ATTRIBUTE(name) \
static Attribute FOO_ ## name ## attr = { \
#name, \
offsetof(FOO, name), \
FOO_ ## name ## _validator \
};
ATTRIBUTE(bar_1);
ATTRIBUTE(bar_2);
#define PY_ATTR_DEF(name) { \
#name, \
(getter)FOO_getter, \
(setter)FOO_setter, \
#name " attribute", \
&(FOO_ ## name ## attr) \
}
static PyGetSetDef FOO_getsetters[] = {
PY_ATTR_DEF(bar_1),
PY_ATTR_DEF(bar_2),
{ NULL }
};
I originally wrote:
resolve_offset surely relies on undefined behaviour, but it should work fine. The alternative would be to have three functions in our attribute object (get, validate, set) instead of one, but that defies the point of the question.
But #tstanisl points out that it looks like it isn't UB. Awesome!

PyObject_CallMethod sometimes seg fault when calling python method

I'm testing a scenario that when C++ set a function pointer to a python class variable, and then use PyObject_CallMethod to run another python method, which contain that class variable.
whole process would like this.
(1). PyCFunction_NewEx() make a py function -> (2). PyDict_SetItemString() assign to class variable under __dict__ -> (3). PyObject_CallMethod() call python method witch contain (1).
When I put all the code inside main() function (whitout void setCallback() and all code inside void setCallback() were placed in main()), It runs perfectly fine. However, after I put some code into a function, sometimes get seg fault, sometimes doesn't call function pointer in python and sometimes get correct answer.
How do I resolve this problem?
C++ Code: main.cpp
#include <python3.7/Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <python3.7/methodobject.h>
// func ptr.
PyObject *myCallFunc(PyObject *self,PyObject *args) {
printf(" aaaaaaaaaaaaaaaaaaaaaaa\n");
return NULL;
}
// def func ptr
typedef PyObject *(*PyCallFunc)(PyObject *self,PyObject *arg);
// set func ptr into python member var
void setCallback(PyObject *ClassObj){
PyCallFunc pyCallFunc = myCallFunc;
PyMethodDef methd = {"methd",pyCallFunc,METH_VARARGS,"py call func"};
PyObject *fName = PyUnicode_FromString(methd.ml_name);
if(fName == NULL) {
printf(" fName\n");
exit(0);
}
PyObject *pyRunFunc = PyCFunction_NewEx(&methd,NULL,fName);
if(pyRunFunc == NULL){
printf(" can not create py function. exit.");
exit(0);
}
Py_DECREF(fName);
PyObject* classAttrDict = PyObject_GetAttrString(ClassObj, "__dict__"); // extract instance Dictionary.
if(classAttrDict == NULL) {
printf(" classAttrDict\n");
exit(0);
}
int pRetSetCurrPrice = PyDict_SetItemString(classAttrDict, "callFunc", pyRunFunc);
if(pRetSetCurrPrice != 0){
printf(" set error. exit.");
exit(0);
}
}
int main(int argc,char **argv){
Py_SetProgramName((wchar_t *)argv[0]);
void *pyMem = PyMem_Malloc(sizeof(wchar_t*)*argc);
wchar_t** _argv = (wchar_t**)&pyMem;
for (int i=0; i<argc; i++) {
wchar_t* arg = Py_DecodeLocale(argv[i], NULL);
_argv[i] = arg;
}
Py_Initialize();
PySys_SetArgv(argc, _argv);
PyObject* programName = PyUnicode_FromString("test");
if(programName == NULL) {
printf(" programName\n");
exit(0);
}
PyObject* pCustomFunc = PyImport_Import(programName); // import test
Py_DECREF(programName);
if(pCustomFunc == NULL) {
printf(" pCustomFunc\n");
exit(0);
}
PyObject* pClass = PyObject_GetAttrString(pCustomFunc, "Test"); // pClass = test.Test
if(pClass == NULL) {
printf(" pClass\n");
exit(0);
}
PyObject* pNewInstance = PyObject_CallObject(pClass,NULL); // pNewInstance = test.Test()
if(pNewInstance == NULL) {
printf(" pNewInstance\n");
exit(0);
}
setCallback(pNewInstance);
PyObject* pCallRet = PyObject_CallMethod(pNewInstance, "runCustomFunc",NULL); // pCallRet = pNewInstance.callFunc()
if(pCallRet == NULL) {
printf(" pCallRet\n");
//exit(0);
}
sleep(2);
printf(" \n\nend\n\n");
Py_Finalize();
return 0;
}
Python code: test.py
import sys
def dummyFunc():
pass
class Test:
def __init__(self):
self.aaa = 0
self.callFunc = dummyFunc
def runCustomFunc(self):
print(" print from python.")
print(" ref count of self.callFunc 1 is %d" %(sys.getrefcount(self.callFunc)))
self.callFunc()
print(" ref count of self.callFunc 2 is %d" %(sys.getrefcount(self.callFunc)))
return 1
cmake for this test project: CMakeLists.txt
# set cmake and compiler.
cmake_minimum_required(VERSION 3.12...3.15)
set(CMAKE_CXX_FLAGS -std=c++17)
# set variable
set(CMAKE_POSITION_INDEPENDENT_CODE ON) # test if this can resolve the problem
set(THREADS_PREFER_PTHREAD_FLAG ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra") # test if optimize cause the problem
set(CMAKE_CXX_FLAGS_DEBUG "-g") # test if optimize cause the problem
set(CMAKE_CXX_FLAGS_RELEASE "-O0") # test if optimize cause the problem
set(LINK_LIB "/usr/local/lib")
set(PYTHON3_LINKER "-lpython3.7")
#set(PTHREAD "-lpthread")
set(PYTHON3_HEADER "/usr/include/python3.7")
set(PYTHON3_LIB "/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu")
set(CPP_FILE_LIST "main.cpp")
include_directories( ${PYTHON3_HEADER})
link_directories( ${PYTHON3_LIB} ${LINK_LIB})
add_executable(pyEmbedFunPtrTest ${CPP_FILE_LIST})
target_link_libraries(pyEmbedFunPtrTest ${PYTHON3_LINKER})
find_package(Threads REQUIRED)
target_link_libraries(pyEmbedFunPtrTest Threads::Threads)
#target_compile_options(pyEmbedFunPtrTest PUBLIC "-pthread")
It could be because the PyMethodDef is created on the stack of the setCallback
You can verify it in the source code of cpython here.
the PyMethodDef is not copied, it is referenced instead.

Python ctypes : OSError undefined symbol when loading library

In Ubuntu 14.04, I wrote a C file called hash.c:
/* hash.c: hash table with linear probing */
typedef struct {
void *key;
void *value;
} ht_entry;
typedef struct {
ht_entry *table;
int len;
int num_entries;
int (*hash_fn)(void *key);
int (*key_cmp)(void *k1, void *k2);
} hashtable;
and compiled it with
gcc -shared hash.c -o test.so -fPIC
Afterwards, I tried to load test.so in a Python script (for testing), but I got the following error: "OSError: .../test.so: undefined symbol: hash_fn"
hash_fn is a function pointer in the hashtable struct. It is referenced a number of times by functions later in the file.
I do not understand why this error is happening. I have Googled but all other cases either concern C++ or includes. In my case I just have 1 C file that includes only stdio and stdlib.
here is the FULL code.
When I comment out all but hash_create and print_info, it loads succesfully. When I uncomment find(), it the error happens.
(print_info is just for testing that ctypes works)
/* hash.c: hash table with linear probing */
#include <stdio.h>
#include <stdlib.h>
typedef struct {
void *key;
void *value;
} ht_entry;
typedef struct {
ht_entry *table;
int len;
int num_entries;
int (*hash_fn)(void *key);
int (*key_cmp)(void *k1, void *k2);
} hashtable;
static void close_gap(hashtable *ht, int i);
static int find(hashtable *ht, void *key);
hashtable* hash_create(int len, int (*hash_fn)(void*), int (*key_cmp)(void*, void*))
{
hashtable* ht = (hashtable*) malloc(sizeof(hashtable));
ht->len = len;
ht->table = calloc(len, sizeof(ht_entry));
ht->hash_fn = hash_fn;
ht->key_cmp = key_cmp;
ht->table[0].key = 2;
ht->table[0].value = 3;
return ht;
}
void print_info(hashtable *ht)
{
printf("%d, %d, %d\n", ht->len, ht->table[0].key, ht->table[0].value);
}
void* hash_retrieve(hashtable* ht, void *key)
{
int i = find(ht, key);
if(i < 0) {
return NULL;
}
return ht->table[i].value;
}
void hash_insert(hashtable* ht, void *key, void *value)
{
if(ht->num_entries == ht->len) {
return;
}
int i = hash_fn(key) % ht->len;
while(ht->table[i].key != NULL) {
i = (i + i) % ht->len;
}
ht->table[i].key = key;
ht->table[i].value = value;
}
void hash_remove(hashtable *ht, void *key)
{
int i = find(ht, key);
if(i < 0) {
return;
}
ht->table[i].key = 0;
ht->table[i].value = 0;
close_gap(ht, i);
}
static int find(hashtable *ht, void *key)
{
int i = hash_fn(key) % ht->len;
int num_checked = 0;
while(ht->table[i].key && num_checked != ht->len) {
if(!ht->key_cmp(ht->table[i].key, key)) {
return i;
}
num_checked++;
i = (i + i) % ht->len;
}
return -1;
}
static void close_gap(hashtable *ht, int i)
{
int j = (i + 1) % ht->len;
while(ht->table[j].key) {
int loc = ht->hash_fn(ht->table[j].key);
if((j > i && (loc <= i || loc > j)) || (j < i && (loc <= i && loc > j))) {
ht->table[i] = ht->table[j];
ht->table[j].key = 0;
ht->table[j].value = 0;
close_gap(ht, j);
return;
}
}
}
When I use your compilation line I get five warnings. There are several problems here. First you are trying to assign an int to void * in several places. That raises a warning, and it would crash at runtime because you are passing 2 and 3 as addresses.
Second, you are calling hash_fn in a couple of places instead of ht->hash_fn. That causes the linker error, but you should consider my other changes, otherwise it will crash at runtime with a SIGSEGV:
/* hash.c: hash table with linear probing */
#include <stdio.h>
#include <stdlib.h>
typedef struct {
void *key;
void *value;
} ht_entry;
typedef struct {
ht_entry *table;
int len;
int num_entries;
int (*hash_fn)(void *key);
int (*key_cmp)(void *k1, void *k2);
} hashtable;
static void close_gap(hashtable *ht, int i);
static int find(hashtable *ht, void *key);
hashtable* hash_create(int len, int (*hash_fn)(void*), int (*key_cmp)(void*, void*))
{
hashtable* ht = (hashtable*) malloc(sizeof(hashtable));
ht->len = len;
ht->table = calloc(len, sizeof(ht_entry));
ht->hash_fn = hash_fn;
ht->key_cmp = key_cmp;
// <<< Code changed here
/*
ht->table[0].key = 2;
ht->table[0].value = 3;
*/
{
int *p = malloc(sizeof(int));
*p = 2;
ht->table[0].key = p;
p = malloc(sizeof(int));
*p = 3;
ht->table[0].value = p;
}
// end of code change
return ht;
}
void print_info(hashtable *ht)
{
// <<<< Code changed
printf("%d, %d, %d\n", ht->len,
*(int *)ht->table[0].key, *(int *)ht->table[0].value);
}
void* hash_retrieve(hashtable* ht, void *key)
{
int i = find(ht, key);
if(i < 0) {
return NULL;
}
return ht->table[i].value;
}
void hash_insert(hashtable* ht, void *key, void *value)
{
if(ht->num_entries == ht->len) {
return;
}
// <<< Code changed
int i = ht->hash_fn(key) % ht->len;
while(ht->table[i].key != NULL) {
i = (i + i) % ht->len;
}
ht->table[i].key = key;
ht->table[i].value = value;
}
void hash_remove(hashtable *ht, void *key)
{
int i = find(ht, key);
if(i < 0) {
return;
ht->table[i].key = 0;
ht->table[i].value = 0;
close_gap(ht, i);
}
static int find(hashtable *ht, void *key)
{
// <<< Code changed
int i = ht->hash_fn(key) % ht->len;
int num_checked = 0;
while(ht->table[i].key && num_checked != ht->len) {
if(!ht->key_cmp(ht->table[i].key, key)) {
return i;
}
num_checked++;
i = (i + i) % ht->len;
}
return -1;
}
static void close_gap(hashtable *ht, int i)
{
int j = (i + 1) % ht->len;
while(ht->table[j].key) {
int loc = ht->hash_fn(ht->table[j].key);
if((j > i && (loc <= i || loc > j)) || (j < i && (loc <= i && loc > j))) {
ht->table[i] = ht->table[j];
ht->table[j].key = 0;
ht->table[j].value = 0;
close_gap(ht, j);
return;
}
}
}
I only coded around the errors and warnings, I did not check the logic. You will see that I have used malloc to allocate memory for key and value. Obviously you will need memory management on these two (i.e. free()).

how can i embed python in c?

i wrote the C code as follow:
int main(int argc, char** argv)
{
PyObject *mod, *name, *func;
Py_Initialize();
mod = PyImport_ImportModule("perf_tester");
if(!mod)
{
printf("cannot find perf_tester.py");
getchar();
return -1;
}
name = PyObject_GetAttrString(mod, "CheckSharpness");
if(!name)
{
printf("can not find CheckSharpness");
getchar();
return -1;
}
func = PyObject_GetAttrString(name,"F");
if(!func)
{
printf("can not find function");
getchar();
return -1;
}
Py_Finalize();
system("pause");
return 0;
}
except for func, i could find mod and name.
and the partial of the perf_tester.py as follow:
def CheckSharpness(sample, edges, min_pass_mtf, min_pass_lowest_mtf,
use_50p, mtf_sample_count, mtf_patch_width,
mtf_crop_ratio=_MTF_DEFAULT_CROP_RATIO,
n_thread=1):
mtfs = [mtf_calculator.Compute(sample, line_start[t], line_end[t],
mtf_patch_width, mtf_crop_ratio,
use_50p)[0] for t in perm]
F = open("data.txt","w")
F.write(str(mtfs))
F.close()
what could I do?
F is a local variable, it's not a member of CheckSharpness. It does not exist when CheckSharpness is not currently running, and each invocation of CheckSharpness has its own copy. There's no way to access this from outside.

Python cannot create instances

I am trying to create a simple Python extension using PyCXX. And I'm compiling against my Python 2.5 installation.
My goal is to be able to do the following in Python:
import Cats
kitty = Cats.Kitty()
if type(kitty) == Cats.Kitty:
kitty.Speak()
But every time I try, this is the error that I get:
TypeError: cannot create 'Kitty' instances
It does see Cats.Kitty as a type object, but I can't create instances of the Kitty class, any ideas?
Here is my current source:
#include "CXX/Objects.hxx"
#include "CXX/Extensions.hxx"
#include <iostream>
using namespace Py;
using namespace std;
class Kitty : public Py::PythonExtension<Kitty>
{
public:
Kitty()
{
}
virtual ~Kitty()
{
}
static void init_type(void)
{
behaviors().name("Kitty");
behaviors().supportGetattr();
add_varargs_method("Speak", &Kitty::Speak);
}
virtual Py::Object getattr( const char *name )
{
return getattr_methods( name );
}
Py::Object Speak( const Py::Tuple &args )
{
cout << "Meow!" << endl;
return Py::None();
}
};
class Cats : public ExtensionModule<Cats>
{
public:
Cats()
: ExtensionModule<Cats>("Cats")
{
Kitty::init_type();
initialize();
Dict d(moduleDictionary());
d["Kitty"] = Type((PyObject*)Kitty::type_object());
}
virtual ~Cats()
{
}
Py::Object factory_Kitty( const Py::Tuple &rargs )
{
return Py::asObject( new Kitty );
}
};
void init_Cats()
{
static Cats* cats = new Cats;
}
int main(int argc, char* argv[])
{
Py_Initialize();
init_Cats();
return Py_Main(argc, argv);
return 0;
}
I do'nt see it in the code, but sort of thing normally means it can't create an instance, which means it can't find a ctor. Are you sure you've got a ctor that exactly matches the expected signature?

Categories