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

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.