5
>>> from ctypes import *
>>> class A(Structure):
... _fields_ = [('a', c_int), ('b', c_int)]
... def __init__(self, x):
... self.a = x + 1
... self.b = x + 2
... 
>>> a = A(1)
>>> a.a
2
>>> a.b
3
>>> b = (A * 2)(1, 2)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: expected A instance, got int

I am trying to construct an array of C structures in python and want the constructor to be called for each element in the array. How can I accomplish this?

martineau
124k29 gold badges181 silver badges319 bronze badges
asked Apr 28, 2016 at 5:45

1 Answer 1

8

The error explains exactly the problem, but in a slightly convoluted manner:

The correct way to initialize such an array is as follows:

structs = [A(1, 2), A(1, 2)]
array = (A * 2)(*structs)

Or, if you can define them all during the array constructor, as follows:

array = (A * 2)(A(1, 2), A(1, 2))

Now, why does this work and your solution not? Since I am passing an instance of A for each argument to the constructor of A * 2, which is an array of A. The array requires an instance of A for each argument passed, which is precisely the error message. It does not expect to convert arguments into A, it expects an instance of A itself.

The step-by-step way is as follows:

from ctypes import *
class A(Structure):
 _fields_ = [('a', c_int), ('b', c_int)]
# this create a class which expects 2 A instances
a_array_factory = A * 2
# our first A instance
struct_1 = A(1, 2)
# our second A instance
struct_2 = A(2, 3)
# initialize our array
array = a_array_factory(struct_1, struct_2)

EDIT:

Since a structure does not have a native Python type, there is no solution, to my knowledge, without creating intermediary structs. According to the Python documentation, the only native types with C primitives are the following:

ctypes type C type Python type
c_bool _Bool bool (1)
c_char char 1-character string
c_wchar wchar_t 1-character unicode string
c_byte char int/long
c_ubyte unsigned char int/long
c_short short int/long
c_ushort unsigned short int/long
c_int int int/long
c_uint unsigned int int/long
c_long long int/long
c_ulong unsigned long int/long
c_longlong __int64 or long long int/long
c_ulonglong unsigned __int64 or unsigned long long int/long
c_float float float
c_double double float
c_longdouble long double float
c_char_p char * (NUL terminated) string or None
c_wchar_p wchar_t * (NUL terminated) unicode or None
c_void_p void * int/long or Nonee or None
answered Apr 28, 2016 at 5:55
Sign up to request clarification or add additional context in comments.

8 Comments

That makes sense ! But there will be an extra intermediate object created for each object in the array. Is there any way to avoid that ? I find that the following works: a = (c_int * 2)(1, 2) So, python interpretor is treating this as equivalent to: a = (c_int * 2)(c_int(1), c_int(2))
c_int has a logical Pythonic type (int). A struct does not. So no. Only values in this table, to my knowledge, can be logically converted (I'll add it to the post): docs.python.org/2/library/ctypes.html#fundamental-data-types
@Rahul, struct_1 and struct_2 are only used to construct array, which has its own buffer. It's basically no different from (c_int * 2)(1, 2), which constructs an array from int objects 1 and 2. The temporary struct_1 and struct_2 objects can be discarded after the array is constructed. If you have an existing object with a writeable buffer interface, you can use (A * 2).from_buffer(x) to avoid temporary objects and copying. The buffer of x must be at least sizeof(A * 2) bytes.
@Rahul, you can also initialize an array using tuples, e.g. (A * 2)((1, 2), (3, 4)), but that's not gaining much in terms efficiency, since internally ctypes constructs a temporary object using each tuple.
@AlexanderHuszagh, I was trying to avoid doing that in the interest of keeping the number of lines of code to a minimum :) But if there is no other way, I may end up doing that.
|

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.