0

Say I have this struct:

typedef struct
{
  PyObject_HEAD
  Foo* myFoo;
} PyFoo;

Let's just say that Foo is:

class Foo
{
public:
  hello()
  {
    std::cout << "Hello\n";
  }
};

I don't want to remake class Foo as a python module because it represents a class from a library with a lot more functions and variables (but that isn't relevant to this question). I didn't really understand from the docs how to create a PyObject* in C/C++ with arguments, much less how to do it with C/C++ pointers as arguments.

I am going off this guide: https://docs.python.org/3/extending/newtypes_tutorial.html

I do have my dealloc, new, and init methods from the guide, but I have not tried to initialize and deallocate any values, except for the instance of the object itself.

This question is similar to Build a PyObject* from a C function? but I want to pass an object pointer instead of a function. I am using the same method as Create an object using Python's C API to create the object, but I don't know how I can give an instance of foo to the PyObject.

2 Answers 2

2

I think you're making things more complicated than needed trying to call the constructor with a pointer. Your tp_new and tp_init methods are designed to provide a Python interface to making an object instance. If it doesn't make sense to provide a Python interface (for example, if your object must always be created with a C++ pointer) then simply don't provide them - set them to NULL and your object will not be creatable from Python.

In C++ you are not restricted to this interface. You can define your own "C++ factory function" taking whatever arguments you like:

PyFoo* make_PyFoo(Foo* myfoo) {

First allocate your object:

PyFoo *obj = (PyFoo*)(type->tp_alloc(type, 0));
# or
PyFoo *obj = PyObject_New(PyFoo, type); # use PyObject_GC_new if it has cyclic references

The two approaches are pretty much equivalent if you haven't defined a custom allocator. Some error-checking has been omitted here....

Next you can simply use your existing Foo* pointer to initialize the relevant field:

obj->myfoo = myfoo;

Then just return obj (and close the bracket).


This answer was inspired largely by my long-standing dislike of Python capsules. It's very rare to see a sensible use-case for them, but people do like using them anyway.

Sign up to request clarification or add additional context in comments.

Comments

0

So you can do this using capsules.

When you create the object:

Foo* myFoo = new Foo();
PyObject* capsule = PyCapsule_New((void*) myFoo, "Foo", NULL);
PyObject* argList = Py_BuildValue("(O)", capsule);
PyObject *obj = PyObject_CallObject((PyObject*) &Foo_PyTypeObject, argList);
Py_DECREF(arglist);
Py_DECREF(capsule); // I don't need python to keep the reference to myFoo

Foo_PyTypeObject should be the PyTypeObject that you make for your extension object.

I then used the capsule in my 'new' function.

Foo_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
  Foo* self;
  self = (Foo*) type->tp_alloc(type, 0);
  if (self != NULL)
  {
    static char *kwlist[] = {const_cast<char*>("Foo"), NULL};

    PyObject* capsule = NULL;
    PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &capsule);
    self->myFoo = (Foo*) PyCapsule_GetPointer(capsule, "Foo"); // this is the myFoo in the PyFoo struct
    Py_DECREF(capsule);
  }
  return (PyObject*) self;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.