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?
Related
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.
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.
I want to know how to wrap a C++ object with Python Extension API (and distutils) without external tools (like Cython, Boost, SWIG, ...). Just in pure Python way without creating a dll.
Note that my C++ object has memory allocations so destructor has to be called to avoid memory leaks.
#include "Voice.h"
namespace transformation
{
Voice::Voice(int fftSize) { mem=new double[fftSize]; }
Voice::~Voice() { delete [] mem; }
int Voice::method1() { /*do stuff*/ return (1); }
}
I just want to do somethings like that in Python :
import voice
v=voice.Voice(512)
result=v.method1()
Seems that the answer was in fact here : https://docs.python.org/3.6/extending/newtypes.html
With examples, but not really easy.
EDIT 1 :
In fact, it is not really for wrapping a C++ object in a Python object, but rather to create a Python object with C code. (edit2 : and so you can wrap C++ object!)
EDIT 2 :
Here is a solution using the Python newtypes
.
Original C++ file : Voice.cpp
#include <cstdio>
#include "Voice.h"
namespace transformation
{
Voice::Voice(int fftSize) {
printf("c++ constructor of voice\n");
this->fftSize=fftSize;
mem=new double[fftSize];
}
Voice::~Voice() { delete [] mem; }
int Voice::filter(int freq) {
printf("c++ voice filter method\n");
return (doubleIt(3));
}
int Voice::doubleIt(int i) { return 2*i; }
}
.
Original h file : Voice.h
namespace transformation {
class Voice {
public:
double *mem;
int fftSize;
Voice(int fftSize);
~Voice();
int filter(int freq);
int doubleIt(int i);
};
}
.
C++ Python wrapper file : voiceWrapper.cpp
#include <Python.h>
#include <cstdio>
//~ #include "structmember.h"
#include "Voice.h"
using transformation::Voice;
typedef struct {
PyObject_HEAD
Voice * ptrObj;
} PyVoice;
static PyModuleDef voicemodule = {
PyModuleDef_HEAD_INIT,
"voice",
"Example module that wrapped a C++ object",
-1,
NULL, NULL, NULL, NULL, NULL
};
static int PyVoice_init(PyVoice *self, PyObject *args, PyObject *kwds)
// initialize PyVoice Object
{
int fftSize;
if (! PyArg_ParseTuple(args, "i", &fftSize))
return -1;
self->ptrObj=new Voice(fftSize);
return 0;
}
static void PyVoice_dealloc(PyVoice * self)
// destruct the object
{
delete self->ptrObj;
Py_TYPE(self)->tp_free(self);
}
static PyObject * PyVoice_filter(PyVoice* self, PyObject* args)
{
int freq;
int retval;
if (! PyArg_ParseTuple(args, "i", &freq))
return Py_False;
retval = (self->ptrObj)->filter(freq);
return Py_BuildValue("i",retval);
}
static PyMethodDef PyVoice_methods[] = {
{ "filter", (PyCFunction)PyVoice_filter, METH_VARARGS, "filter the mem voice" },
{NULL} /* Sentinel */
};
static PyTypeObject PyVoiceType = { PyVarObject_HEAD_INIT(NULL, 0)
"voice.Voice" /* tp_name */
};
PyMODINIT_FUNC PyInit_voice(void)
// create the module
{
PyObject* m;
PyVoiceType.tp_new = PyType_GenericNew;
PyVoiceType.tp_basicsize=sizeof(PyVoice);
PyVoiceType.tp_dealloc=(destructor) PyVoice_dealloc;
PyVoiceType.tp_flags=Py_TPFLAGS_DEFAULT;
PyVoiceType.tp_doc="Voice objects";
PyVoiceType.tp_methods=PyVoice_methods;
//~ PyVoiceType.tp_members=Noddy_members;
PyVoiceType.tp_init=(initproc)PyVoice_init;
if (PyType_Ready(&PyVoiceType) < 0)
return NULL;
m = PyModule_Create(&voicemodule);
if (m == NULL)
return NULL;
Py_INCREF(&PyVoiceType);
PyModule_AddObject(m, "Voice", (PyObject *)&PyVoiceType); // Add Voice object to the module
return m;
}
.
distutils file : setup.py
from distutils.core import setup, Extension
setup(name='voicePkg', version='1.0', \
ext_modules=[Extension('voice', ['voiceWrapper.cpp','Voice.cpp'])])
.
python test file : test.py
import voice
v=voice.Voice(512)
result=v.filter(5)
print('result='+str(result))
.
and magic :
sudo python3 setup.py install
python3 test.py
Output is :
c++ constructor of voice
c++ voice filter method
result=6
Enjoy !
Doom
I'm having an issue where a python class, which is derived from a c++ base class using pybind11, is being immediately destructed (garbage collected). I would like C++ to take ownership of the dynamically allocated object, but I can't seem to make that happen. I've tried keep_alive, passing shared_ptr<> as py::class_ template argument, and py::return_value_policy... nothing is working. I suspect this is just user error.
This is a simplification of the real issue I'm having with a much larger code base that is architected similarly. Changing the architecture is not an option, so making this example work is critical for me.
I have two c++ classes that I have created python interfaces for using pybind11. Class A and B both have virtual methods, so they have corresponding trampoline classes to support inheritance. The user calls the B::Run() function which results in a dynamically allocated (via new) A object to be created. When I create specializations of these two classes in python, as shown below.... Segmentation fault because the B::aBase is destroyed immediately after B::Run being called.
Any Ideas how to fix this? Thanks in advance!
class A
{
public:
A(){};
virtual ~A()
{
std::cout << "In A::~A()\n";
};
virtual char* SayHello()
{
char* x = "\n\nHello from Class A\n\n";
return x;
}
};
class ATramploline : public A
{
public:
using A::A;
char* SayHello() override
{
PYBIND11_OVERLOAD( char*,A,SayHello,);
}
};
class B
{
public:
B()
{
std::cout << "In Class B Constructor\n";
}
void Run()
{
aBase = AllocateAnAClass();
std::cout << aBase->SayHello();
}
virtual ~B()
{
fprintf(stderr,"About to delete aBase");
delete aBase;
}
A* aBase;
virtual A* AllocateAnAClass()
{
return new A;
}
};
class BTramploline : public B
{
public:
using B::B;
A* AllocateAnAClass() override
{
PYBIND11_OVERLOAD( A*,B,AllocateAnAClass,);
}
};
PYBIND11_MODULE(TestModule,m)
{
py::class_<A,ATramploline>(m,"A")
.def(py::init<>(),py::return_value_policy::reference_internal)
.def("SayHello",&A::SayHello);
py::class_<B,BTramploline>(m,"B")
.def(py::init<>())
.def("Run",&B::Run)
.def("AllocateAnAClass",&B::AllocateAnAClass,py::return_value_policy::reference_internal);
}
#!/usr/bin/python3
from TestModule import A,B
class MyA(A):
def __init__(self):
super().__init__()
print("Done with MyA Constructor")
def SayHello(self):
return '\n\nHello from Class MyA\n\n'
class MyB(B):
def __init__(self):
super().__init__()
print("Done With MyB Constructor")
def AllocateAnAClass(self):
print("In MyB::AllocateAnAClass!!!")
return MyA()
#x = B()
#x.Run()
y = MyB()
y.Run()
print("done with test script\n")
The correct (I think) way to use std::shared_ptr<A> as the A holder is to add it to class_<A...> arguments.
You also want to replace every instance of A* with std::shared_ptr<A>, and new with std::make_shared. I think non-default return policies are not needed in this case, so I have removed them; YMMV.
Working module below (with minor errors corrected).
#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>
namespace py = pybind11;
class A
{
public:
A(){};
A(const A&) { std::cout << "Copying A\n"; }
virtual ~A()
{
std::cout << "In A::~A()\n";
};
virtual const char* SayHello()
{
const char* x = "\n\nHello from Class A\n\n";
return x;
}
};
class ATrampoline : public A
{
public:
using A::A;
const char* SayHello() override
{
PYBIND11_OVERLOAD( const char*,A,SayHello,);
}
};
class B
{
public:
B()
{
std::cout << "In Class B Constructor\n";
}
B(const B&) { std::cout << "Copying B\n"; }
void Run()
{
aBase = AllocateAnAClass();
std::cout << aBase->SayHello();
}
virtual ~B()
{
}
std::shared_ptr<A> aBase;
virtual std::shared_ptr<A> AllocateAnAClass()
{
return std::make_shared<A>();
}
};
class BTrampoline : public B
{
public:
using B::B;
std::shared_ptr<A> AllocateAnAClass() override
{
PYBIND11_OVERLOAD(std::shared_ptr<A>,B,AllocateAnAClass,);
}
};
PYBIND11_MODULE(TestModule,m)
{
py::class_<A,std::shared_ptr<A>, ATrampoline>(m,"A")
.def(py::init<>())
.def("SayHello",&A::SayHello);
py::class_<B, BTrampoline>(m,"B")
.def(py::init<>())
.def("Run",&B::Run)
.def("AllocateAnAClass",&B::AllocateAnAClass);
}
py::nodelete was the solution. While n.m's answer DOES work, it would require going back and chaning all of the pointer in an existing libary to smart pointers, which isn't a viable option for me. Using py::nodelete allows me to do everything on the pybind11 side.
py::class_<A,ATramploline,std::unique_ptr<A,py::nodelete> >(m,"A")
.def(py::init<>())
.def("SayHello",&A::SayHello);
I'm trying to export some C++ functions into python, and it's working pretty well most of the time, but I'm having issues when a function takes more than on string in parameters, the first one is always ok, but the other ones are always gibberish.
C++ code:
#include <Python.h>
#include <iostream>
PyObject* wrap_one_string(PyObject *self, PyObject *args)
{
char* str;
if (!PyArg_ParseTuple(args,"s:wrap_one_string",&str))
{
return nullptr;
}
std::cout << str << std::endl;
return Py_None;
}
PyObject* wrap_two_string(PyObject *self, PyObject *args)
{
char* str1, str2;
if (!PyArg_ParseTuple(args,"ss:wrap_one_string", &str1, &str2))
{
return nullptr;
}
std::cout << str1 << std::endl << str2 << std::endl;
return Py_None;
}
static PyMethodDef exampleMethods[] = {
{ "one_string", wrap_one_string, METH_VARARGS, nullptr },
{ "two_string", wrap_two_string, METH_VARARGS, nullptr },
{ nullptr, nullptr, 0, nullptr}
};
extern "C"
{
__declspec(dllexport)
void initexample()
{
PyObject *m;
m = Py_InitModule("example", exampleMethods);
}
}
python code:
import example
example.one_string("ab")
example.two_string("abcd", "efgh")
And the result is:
ab
abcd
È
The second string parameter is always a weird character.
Any idea where this might come from?
Thanks
Ah, nevermind, dumb mistake from my part
char* str1, str2;
should be
char* str1, *str2;
Too bad it compiled, though it did give an error using
str2 = PyString_AsString(PyTuple_GET_ITEM(args, 1))
to access the second string.
One small note, remember to Py_INCREF(Py_None); before returning Py_None, as stated in the reference [https://docs.python.org/2/extending/extending.html#back-to-the-example]
Or use Py_RETURN_NONE macro [https://docs.python.org/2/c-api/none.html#c.Py_RETURN_NONE]