Python object type 'A' cannot be used as a template argument

969 views
Skip to first unread message

Eric Reynolds

unread,
Jul 15, 2015, 7:30:08 AM7/15/15
to cython...@googlegroups.com
Hello everyone,

Here's my code:

# vector.pyx
from libcpp.vector cimport vector

cdef class A:
    cdef int x
  
cdef vector[A] vect

This doesn't compile under 0.22.1, in fact `cython --cplus vector.pyx` gives the following error:

Error compiling Cython file:
------------------------------------------------------------
...
from libcpp.vector cimport vector

cdef class A:
    cdef int x

cdef vector[A] vect
           ^
------------------------------------------------------------

vector.pyx:6:12: Python object type 'A' cannot be used as a template argument

But this worked perfectly in earlier versions of Cython (I have checked using 0.15.1), and generated the statically typed C++ code I would have expected it to generate.

Is this intentional or is it a bug?

Thanks, regards,

Eric

Eric Reynolds

unread,
Jul 15, 2015, 8:46:39 AM7/15/15
to cython...@googlegroups.com
I have pin-pointed when this change happened: in 0.19.2 my code compiles, in 0.20 it doesn't. The release notes for 0.20 do not seem to mention anything regarding this as far as I can tell.

Eric Reynolds

unread,
Jul 15, 2015, 10:31:17 AM7/15/15
to cython...@googlegroups.com
Indeed the commit which introduced this seems to be https://github.com/cython/cython/commit/870aedffbd5a1839ea468c27906e63422c111240

So it's intentional apparently. But I don't understand why this language feature should be disabled.

Stefan Behnel

unread,
Jul 15, 2015, 10:37:14 AM7/15/15
to cython...@googlegroups.com
> Indeed the commit which introduced this seems to
> be https://github.com/cython/cython/commit/870aedffbd5a1839ea468c27906e63422c111240
>
> So it's intentional apparently. But I don't understand why this language
> feature should be disabled.

C++ doesn't know about CPython reference counting, so you will end up
leaking object references and thus memory. Or you'd store a pointer there,
accidentally loose the last reference to the object and get a crash. Both
options are far from what I would call "worked perfectly".

You can cast the objects to <void*> or <PyObject*> to store them in the C++
vector, and then make sure you keep a live reference to them around.

However, why would you do that in the first place? Why not just use a
Python list instead, which is automatically memory managed for you?

Stefan

Eric Reynolds

unread,
Jul 15, 2015, 10:59:29 AM7/15/15
to cython...@googlegroups.com, stef...@behnel.de
Hi Stefan,

Thanks for your reply.

I think I see your point - though I am a total Cython newbie so I definitely need to think about it a bit more.

The reason I am looking to do something like this is that having a background in statically typed languages (like C++ and C#) it seems totally natural to me that one should not only want typed scalar references (i.e. pointers) but also typed containers (e.g. lists of T, maps of S -> T and so on). I am just starting out in Cython so it could well be that I've missed something obvious, but judging by this SO question others have also encountered this perplexity and haven't really had their question answered.

The problem with a list obviously isn't a big deal, except that once you've stored your typed object in there it's boxed and Cython no longer knows what type it is, so it doesn't optimize member lookups and so on. So, my understanding is, to induce optimized C-level member access you need to unbox it by casting back to your type every time you use it in your code, which seems unnecessarily tedious (as well as having a minor performance impact, if Cython does a dynamic type check - I haven't tried yet).

But like I said, I'm a newbie I'm probably missing something obvious.

Thanks again!

Regards,

Eric

Daniele Nicolodi

unread,
Jul 15, 2015, 11:16:26 AM7/15/15
to cython...@googlegroups.com
On 15/07/15 16:52, Eric Reynolds wrote:
> The reason I am looking to do something like this is that having a
> background in statically typed languages (like C++ and C#) it seems
> totally natural to me that one should not only want typed scalar
> references (i.e. pointers) but also typed containers (e.g. lists of T,
> maps of S -> T and so on). I am just starting out in Cython so it could
> well be that I've missed something obvious, but judging by this SO
> question
> <http://stackoverflow.com/questions/6997102/cython-is-there-a-way-to-create-lists-of-only-one-type-unlike-python-lists>
> others have also encountered this perplexity and haven't really had
> their question answered.

If you need typed containers you can use C++ standard library containers
from Cython just fine. However, Python classes and Cython extension
classes are not C++ classes and thus have different semantics and cannot
(and should not) be interchangeable.

> The problem with a list obviously isn't a big deal, except that once
> you've stored your typed object in there it's boxed and Cython no longer
> knows what type it is, so it doesn't optimize member lookups and so on.
> So, my understanding is, to induce optimized C-level member access you
> need to unbox it by casting back to your type every time you use it in
> your code, which seems unnecessarily tedious (as well as having a minor
> performance impact, if Cython does a dynamic type check - I haven't
> tried yet).
>
> But like I said, I'm a newbie I'm probably missing something obvious.

The fundamental misconception is that Cython is not an alternate syntax
for C++. If you want to code with C++ semantics you are much better
served writing C++ code, rather than abusing Cython.

That said, you can code your Cython typed-list implementation, but I
believe you would have only marginal performance benefits over the
standard python list.

Cheers,
Daniele

Ian Henriksen

unread,
Jul 15, 2015, 1:35:58 PM7/15/15
to cython...@googlegroups.com
On Wed, Jul 15, 2015 at 8:59 AM Eric Reynolds <eric.remo...@gmail.com> wrote:
Hi Stefan,

Thanks for your reply.

I think I see your point - though I am a total Cython newbie so I definitely need to think about it a bit more.

The reason I am looking to do something like this is that having a background in statically typed languages (like C++ and C#) it seems totally natural to me that one should not only want typed scalar references (i.e. pointers) but also typed containers (e.g. lists of T, maps of S -> T and so on). I am just starting out in Cython so it could well be that I've missed something obvious, but judging by this SO question others have also encountered this perplexity and haven't really had their question answered.

The problem with a list obviously isn't a big deal, except that once you've stored your typed object in there it's boxed and Cython no longer knows what type it is, so it doesn't optimize member lookups and so on. So, my understanding is, to induce optimized C-level member access you need to unbox it by casting back to your type every time you use it in your code, which seems unnecessarily tedious (as well as having a minor performance impact, if Cython does a dynamic type check - I haven't tried yet).

This seems like a perfectly valid concern to me, though it would be
nice to have a better idea of how much the additional type checking
matters. If I remember correctly, bypassing this extra type checking is
one of the primary reasons memoryviews were introduced. If you're
working with wrapper classes for something at the C/C++ level you can
work around this by only storing the wrapped objects inside a vector
and then rebuilding the Python objects from them when you need
some Python-specific functionality. You could probably even serialize more generic kinds of objects as structs, but it would be nice if making
a list-like data structure that holds nontrivial Cython extension types
were less painful.
Best of luck,
-Ian Henriksen
 
--

---
You received this message because you are subscribed to the Google Groups "cython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cython-users...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Robert Bradshaw

unread,
Jul 15, 2015, 11:33:04 PM7/15/15
to cython...@googlegroups.com
No, though there are other overheads that can be eliminated.

> If you're
> working with wrapper classes for something at the C/C++ level you can
> work around this by only storing the wrapped objects inside a vector
> and then rebuilding the Python objects from them when you need
> some Python-specific functionality. You could probably even serialize more
> generic kinds of objects as structs, but it would be nice if making
> a list-like data structure that holds nontrivial Cython extension types
> were less painful.

It's not too hard to make a reference-counting "smart" pointer (I've
done this several times myself) that would hold a Python reference. It
would require special support from Cython to allow it to be typed for
an extension type. What would we want to call it?

>> But like I said, I'm a newbie I'm probably missing something obvious.

It's not obvious, it just doesn't fit well into the type system. E.g.
one might want a "typed" list, but there's no way to enforce that only
objects of the right type would be inserted.

Eric Reynolds

unread,
Jul 16, 2015, 2:58:23 AM7/16/15
to cython...@googlegroups.com


On Wednesday, 15 July 2015 17:16:26 UTC+2, Daniele Nicolodi wrote:
If you need typed containers you can use C++ standard library containers
from Cython just fine. However, Python classes and Cython extension
classes are not C++ classes and thus have different semantics and cannot
(and should not) be interchangeable.

I want typed containers of Cython extension classes. They don't have to be std::vector, but it's the only thing I've found so far that vaguely works out-of-the-box on versions <0.20 (though I accept that their use would actually be buggy as Stefan points out)

The fundamental misconception is that Cython is not an alternate syntax
for C++. If you want to code with C++ semantics you are much better
served writing C++ code, rather than abusing Cython.

That's not my misconception. I don't want to use Cython as an alternate syntax for C++. I want typed containers of Cython extension classes, not just basic types like int or float or structs. They don't have to be C++ containers, I am open to any viable alternative.

That said, you can code your Cython typed-list implementation, but I
believe you would have only marginal performance benefits over the
standard python list.

Python list 8.45900011063 s
Typed list 1.62400007248 s

More than 4x doesn't seem marginal to me :)

My code:

# vector.pyx

cdef class A:
    cdef public int x
    
cdef class Alist:
    def __init__(self):
            self.inner = []
    cdef list inner
    cdef void append(self, A a):
        self.inner.append(a)
    cdef A get(self, int i):
        return <A> self.inner[i]
    def __len__(self):
        return len(self.inner)

cpdef Alist make_typed_list(int N):
    cdef A a
    cdef int i
    cdef Alist L = Alist()
    for i in range(N):
        a = A()
        a.x = 1
        L.append(a)
    return L
    
cpdef list make_python_list(int N):
    cdef A a
    cdef int i
    cdef list L = []
    for i in range(N):
        a = A()
        a.x = 1
        L.append(a)
    return L

cpdef long test_python_list(list L) except -1:
    cdef int i
    cdef long sum = 0
    for i in range(len(L)):
        sum += L[i].x
    return sum

cpdef long test_typed_list(Alist L) except -1:
    cdef int i
    cdef long sum = 0
    for i in range(len(L)):
        sum += L.get(i).x
    return sum

# test_vector.py

import pyximport
pyximport.install()

from vector import *
from time import time

L = make_python_list(10000000)
start = time()
z = test_python_list(L)
end = time()
print "Python list", end - start, "s"

L = make_typed_list(10000000)
start = time()
z = test_typed_list(L)
end = time()
print "Typed list", end - start, "s" 


Eric Reynolds

unread,
Jul 16, 2015, 3:00:39 AM7/16/15
to cython...@googlegroups.com
Hello everyone,

Thanks for your kind replies. I had actually posted a performance test I did yesterday, but it never appeared in the user group, I'm not sure what happened to that.
 
It's not too hard to make a reference-counting "smart" pointer (I've
done this several times myself) that would hold a Python reference. It
would require special support from Cython to allow it to be typed for
an extension type. What would we want to call it?

But what about cycles? Surely the only way to break cycles is to make the Python GC aware of the dependency tree, which I would have thought is very difficult to do automatically given that C/C++ doesn't have any serious reflection functionality.

It seems to me that using Python containers is not too bad, because you can always cast the boxed object to the right type before using it (from what I could tell, the generated C code does a static cast without any runtime type checking - which is high-performance but of course also potentially dangerous). To facilitate this one can write a wrapper class around a Python list. What's tedious though is having to rewrite this wrapper class for each element type. Is there any solution to this? (e.g. generics or templates or something?)

Thanks again,

Eric

Stefan Behnel

unread,
Jul 16, 2015, 3:11:55 AM7/16/15
to cython...@googlegroups.com
I'm sure this becomes much faster if you spell it the obvious way:

for a in L:
sum += (<A>a).x

If you want safety (and yes, you want that for Python functions), you can
use "(<A?>).x" instead.

If that additional type check is actually noticeable (not sure if it is),
you can still wrap the list in a cdef class that prevents Python objects
from entering illegal data, and then use unchecked code on the plain list
internally.

Stefan

Robert Bradshaw

unread,
Jul 16, 2015, 3:19:41 AM7/16/15
to cython...@googlegroups.com
On Wed, Jul 15, 2015 at 11:46 PM, Eric Reynolds
<eric.remo...@gmail.com> wrote:
> Hello everyone,
>
> Thanks for your kind replies. I had actually posted a performance test I did
> yesterday, but it never appeared in the user group, I'm not sure what
> happened to that.

Posts from new users are moderated, though usually quicker than than.

I see your timings now, the slowdown is because L[i].x is accessing x
as a Python object, (<A>L[i]).x would be much faster.

>> It's not too hard to make a reference-counting "smart" pointer (I've
>> done this several times myself) that would hold a Python reference. It
>> would require special support from Cython to allow it to be typed for
>> an extension type. What would we want to call it?
>
>
> But what about cycles? Surely the only way to break cycles is to make the
> Python GC aware of the dependency tree, which I would have thought is very
> difficult to do automatically given that C/C++ doesn't have any serious
> reflection functionality.

It's correct, not optimal. Yes, if you have cycles involving these C++
containers they won't get freed.

> It seems to me that using Python containers is not too bad, because you can
> always cast the boxed object to the right type before using it (from what I
> could tell, the generated C code does a static cast without any runtime type
> checking - which is high-performance but of course also potentially
> dangerous). To facilitate this one can write a wrapper class around a Python
> list. What's tedious though is having to rewrite this wrapper class for each
> element type. Is there any solution to this? (e.g. generics or templates or
> something?)

Use a template language of your choice to create the .pyx files. Or do
the (safe or unsafe) cast after accessing the item.

Eventually we'll probably want to support PEP-484 style annotations
for containers, probably with type checking by default and a directive
to disable.

- Robert

Eric Reynolds

unread,
Jul 16, 2015, 3:38:14 AM7/16/15
to cython...@googlegroups.com
Use a template language of your choice to create the .pyx files. Or do
the (safe or unsafe) cast after accessing the item.

Ok awesome, thanks for the suggestions! I was not aware of the <A?> cast, that's good to know.

If at some point I have a bit of time to dedicate to this, I'll write a bit of documentation about this aspect and make a PR so you can include it in the Cython docs if you see fit, since I wasn't able to find anything regarding this particular use case. I am surprised in fact that it hasn't been brought up more often!

Thanks again for all your help, regards,

Eric.

Ian Henriksen

unread,
Jul 16, 2015, 4:28:25 PM7/16/15
to cython...@googlegroups.com


On Thursday, July 16, 2015 at 1:19:41 AM UTC-6, Robert Bradshaw wrote:

Eventually we'll probably want to support PEP-484 style annotations
for containers, probably with type checking by default and a directive
to disable.

- Robert

Yep, that's probably the best way to do this in the long run.

It might be good at some point to make extension types that wrap
some of the C++ standard containers, handle reference counting,
and force a given type to be used. I don't see myself adding this
feature in the immediate future though.

Thanks!
-Ian

Stefan Behnel

unread,
Jul 17, 2015, 12:34:37 AM7/17/15
to cython...@googlegroups.com
Ian Henriksen schrieb am 16.07.2015 um 22:28:
> On Thursday, July 16, 2015 at 1:19:41 AM UTC-6, Robert Bradshaw wrote:
>> Eventually we'll probably want to support PEP-484 style annotations
>> for containers, probably with type checking by default and a directive
>> to disable.
>
> Yep, that's probably the best way to do this in the long run.
>
> It might be good at some point to make extension types that wrap
> some of the C++ standard containers, handle reference counting,
> and force a given type to be used. I don't see myself adding this
> feature in the immediate future though.

Note that the compiler allows you to implement this in Cython rather than
C, even using templating. See CppConvert.pyx, for example.

Stefan

Reply all
Reply to author
Forward
0 new messages