1

foo.h

typedef int bar;
typedef struct _foo
{
    bar* b;
} foo;
extern foo* foo_new();
extern bar* foo_bar_new(foo* f);

foo.pxd

cdef extern from "foo.h":
    ctypedef int bar;
    ctypedef struct foo:
        bar* b
    foo* foo_new();
    bar* foo_bar_new(foo* f);

foo.pyx

from foo cimport *

cdef class Foo:
    cdef foo* _ptr
    def __cinit__(self):
        self._ptr = foo_new()

cdef class Bar:
    cdef bar* _ptr
    def __cinit__(self, f):
        self._ptr = foo_bar_new(f._ptr)   # error
                                ^
foo.pyx:11:33: Cannot convert Python object to 'foo *'

cython foo.pyx throws an error at the marked line.

I'm not quite sure what I'm doing wrong. Changing _ptr in Foo to cpdef results in the same error.

== Update == I changed foo.pyx to the following and it works.

from foo cimport *

cdef class Foo:
    cdef foo* _ptr
    def __cinit__(self):
        self._ptr = foo_new()

    def new_bar(self):
        cdef bar* b
        b = foo_bar_new(self._ptr)
        tmp = Bar()
        tmp._set(b)
        return tmp

cdef class Bar:
    cdef bar* _ptr
    cdef _set(self, bar* bptr):
        self._ptr = bptr

    def get(self):
        return bar_get(self._ptr)

I can do f=Foo(); b=f.new_bar() but this doesn't seem ideal to me since I can't create with Bar(f) or similar. The problem is that I can't cdef __cinit__() since it's a special method. Can't cdef __init__() either. Any ideas on how to solve this problem?

5
  • UPDATE: I managed to get it to compile but it's not ideal. Updates in the original. Commented Dec 20, 2017 at 15:52
  • Possible duplicate of Initializing Cython objects with existing C Objects Commented Dec 21, 2017 at 14:22
  • In short, use factory functions that take a foo* argument and return a new Foo after assigning Foo._ptr to the passed in foo*. This is needed as __init__ can only accept python objects, not C types. Commented Dec 21, 2017 at 14:25
  • @danny I agree with you about factory functions being an option. However, my reading of the initial question is that OP was trying to pass Cython objects rather than pointers so I don't think your duplicate suggestion quite fits. It fits a bit better with the edit, but I think that's a workround more than what they're trying to do. Commented Dec 21, 2017 at 17:29
  • The OP specifically asks how to create Cython object from a pointer, therefore is a duplicate of existing answer. Commented Dec 27, 2017 at 10:21

2 Answers 2

1

You need to ensure it knows what type f is, at compile time:

def __cinit__(self, Foo f):
    self._ptr = foo_bar_new(f._ptr)   # error

without doing that it has to assume that _ptr is a standard Python attribute lookup (done at runtime), and so will be a standard Python object. If you specify that f is a Foo then it's able to use the known definition of _ptr.

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

3 Comments

The above will allow Bar(Foo()) to be used but not Bar(f) where f is of type foo*.
Since __cinit__ is a def function in can only be called with Python types anyway. There isn't a particularly good method for constructing Cython objects directly from c types.
'Particularly good' is relevant, though there is a method for doing that which have talked about before.
0

I've been using a modification of the factory function model that seems to work ok.

cdef class Bar:
    cdef bar* _ptr
    def __cinit__(self):
        self._ptr = NULL

    cdef from_ptr(self, bar* ptr):
        self._ptr = ptr
        return self

cdef class Foo:
    cdef foo* _ptr
    def new_bar(self):
        cdef bar* b
        b = foo_bar_new(self._ptr)
        return Bar().from_ptr(b)

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.