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?

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
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.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.