1 [SWIG][] is a Simplified Wrapper and Interface Generator. It makes it
2 very easy to provide a quick-and-dirty wrapper so you can call code
3 written in [[C]] or [[C++|Cpp]] from code written in another
4 (e.g. [[Python]]). I don't do much with SWIG, because while building
5 an object oriented wrapper in SWIG is possible, I could never get it
6 to feel natural (I like [[Cython]] better). Here are my notes from
7 when I *do* have to interact with SWIG.
9 `%array_class` and memory management
10 ====================================
12 `%array_class` (defined in [carrays.i][]) lets you wrap a C array in
13 a class-based interface. The example from the [docs][] is nice and
14 concise, but I was [running into problems][comedilib-patch].
19 >>> data = example.sample_array(n)
20 >>> for i in range(n):
22 >>> example.print_sample_pointer(n, data)
23 Traceback (most recent call last):
25 TypeError: in method 'print_sample_pointer', argument 2 of type 'sample_t *'
27 I just bumped into these errors again while trying to add an
28 `insn_array` class to [[Comedi]]'s wrapper:
30 %array_class(comedi_insn, insn_array);
32 so I decided it was time to buckle down and figure out what was going
33 on. All of the non-Comedi examples here are based on my [[example
34 test code|array_class-example.tar.gz]].
36 The basic problem is that while you and I realize that an
37 `array_class`-based instance is interchangable with the underlying
38 pointer, SWIG does not. For example, I've defined a `sample_vector_t`
41 typedef double sample_t;
42 typedef struct sample_vector_struct {
47 and a `sample_array` class:
49 %array_class(sample_t, sample_array);
51 A bare instance of the double array class has fancy SWIG additions for
52 getting and setting attributes. The class that adds the extra goodies
53 is SWIG's *proxy class*:
55 >>> print(data) # doctest: +ELLIPSIS
56 <example.sample_array; proxy of <Swig Object of type 'sample_array *' at 0x...> >
58 However, C functions and structs interact with the bare pointer
59 (i.e. without the proxy goodies). You can use the `.cast()` method to
62 >>> data.cast() # doctest: +ELLIPSIS
63 <Swig Object of type 'double *' at 0x...>
64 >>> example.print_sample_pointer(n, data.cast())
65 >>> vector = example.sample_vector_t()
67 >>> vector.data = data
68 Traceback (most recent call last):
70 TypeError: in method 'sample_vector_t_data_set', argument 2 of type 'sample_t *'
71 >>> vector.data = data.cast()
72 >>> vector.data # doctest: +ELLIPSIS
73 <Swig Object of type 'double *' at 0x...>
75 So `.cast()` gets you from `proxy of <Swig Object ...>` to `<Swig
76 Object ...>`. How you go the other way? You'll need this if you want
77 to do something extra fancy, like accessing the array members ;).
80 Traceback (most recent call last):
82 TypeError: 'SwigPyObject' object is not subscriptable
84 The answer here is the `.frompointer()` method, which can function as
85 a [class method][classmethod]:
87 >>> reconst_data = example.sample_array.frompointer(vector.data)
93 >>> example.sample_array.frompointer(vector.data)[n-1]
96 I chose the somewhat awkward name of `reconst_data` for the
97 reconstitued data, because if you use `data`, you clobber the earlier
98 `example.sample_array(n)` definition. After the clobber, Python
99 garbage collects the old `data`, and becase the old data claims it
100 owns the underlying memory, Python frees the memory. This leaves
101 `vector.data` and `reconst_data` pointing to unallocated memory, which
102 is probably not what you want. If keeping references to the original
103 objects (like I did above with `data`) is too annoying, you have to
104 manually tweak the [ownership flag][ownership]:
108 >>> data.thisown = False
109 >>> data = example.sample_array.frompointer(vector.data)
113 This way, when `data` is clobbered, SWIG doesn't release the
114 underlying array (because `data` no longer claims to own the array).
115 However, `vector` doesn't own the array either, so you'll have to
116 remember to reattach the array to somthing that will clean it up
117 before vector goes out of scope to avoid leaking memory:
119 >>> data.thisown = True
122 For deeply nested structures, this can be annoying, but it will work.
124 <!-- test these doctests from the SWIG directory with
125 LD_LIBRARY_PATH=. nosetests --with-doctest --doctest-extension=mdwn ../SWIG.mdwn
132 [SWIG]: http://www.swig.org/
133 [carrays.i]: http://www.swig.org/Doc2.0/Library.html#Library_carrays
134 [docs]: http://www.swig.org/Doc2.0/Library.html#Library_carrays
135 [comedilib-patch]: http://comedi.org/git?p=comedi/comedilib.git;a=blobdiff;f=swig/comedi.i;h=5da6160d91d206d007e20c9ac5091d1735afdd30;hp=581997542927fd31cd2e0d03c220377774cfa600;hb=3fe8e6baac051d80906c6fac6c18c04c8df9ce4a;hpb=880074831499ba68c17a1c2653d71d6eef3b9cfb
136 [classmethod]: http://docs.python.org/library/functions.html#classmethod
137 [ownership]: http://www.swig.org/Doc2.0/Python.html#Python_nn30