--- /dev/null
+[SWIG][] is a Simplified Wrapper and Interface Generator. It makes it
+very easy to provide a quick-and-dirty wrapper so you can call code
+written in [[C]] or [[C++|Cpp]] from code written in another
+(e.g. [[Python]]). I don't do much with SWIG, because while building
+an object oriented wrapper in SWIG is possible, I could never get it
+to feel natural (I like [[Cython]] better). Here are my notes from
+when I *do* have to interact with SWIG.
+
+`%array_class` and memory management
+====================================
+
+`%array_class` (defined in [`carrays.i`][]) lets you wrap a C array in
+a class-based interface. The example from the [docs][] is nice and
+concise, but I was [running into problems][comedilib-patch].
+
+
+ >>> import example
+ >>> n = 3
+ >>> data = example.sample_array(n)
+ >>> for i in range(n):
+ ... data[i] = 2*i + 3
+ >>> example.print_sample_pointer(n, data)
+ Traceback (most recent call last):
+ ...
+ TypeError: in method 'print_sample_pointer', argument 2 of type 'sample_t *'
+
+I just bumped into these errors again while trying to add an
+`insn_array` class to [[Comedi]]'s wrapper:
+
+ %array_class(comedi_insn, insn_array);
+
+so I decided it was time to buckle down and figure out what was going
+on. All of the non-Comedi examples here are based on my [[example
+test code|array_class-example.tar.gz]].
+
+The basic problem is that while you and I realize that an
+`array_class`-based instance is interchangable with the underlying
+pointer, SWIG does not. For example, I've defined a `sample_vector_t`
+`struct`:
+
+ typedef double sample_t;
+ typedef struct sample_vector_struct {
+ size_t n;
+ sample_t *data;
+ } sample_vector_t;
+
+and a `sample_array` class:
+
+ %array_class(sample_t, sample_array);
+
+A bare instance of the double array class has fancy SWIG additions for
+getting and setting attributes. The class that adds the extra goodies
+is SWIG's *proxy class*:
+
+ >>> print(data) # doctest: +ELLIPSIS
+ <example.sample_array; proxy of <Swig Object of type 'sample_array *' at 0x...> >
+
+However, C functions and structs interact with the bare pointer
+(i.e. without the proxy goodies). You can use the `.cast()` method to
+remove the goodies:
+
+ >>> data.cast() # doctest: +ELLIPSIS
+ <Swig Object of type 'double *' at 0x...>
+ >>> example.print_sample_pointer(n, data.cast())
+ >>> vector = example.sample_vector_t()
+ >>> vector.n = n
+ >>> vector.data = data
+ Traceback (most recent call last):
+ ...
+ TypeError: in method 'sample_vector_t_data_set', argument 2 of type 'sample_t *'
+ >>> vector.data = data.cast()
+ >>> vector.data # doctest: +ELLIPSIS
+ <Swig Object of type 'double *' at 0x...>
+
+So `.cast()` gets you from `proxy of <Swig Object ...>` to `<Swig
+Object ...>`. How you go the other way? You'll need this if you want
+to do something extra fancy, like accessing the array members ;).
+
+ >>> vector.data[0]
+ Traceback (most recent call last):
+ ...
+ TypeError: 'SwigPyObject' object is not subscriptable
+
+The answer here is the `.frompointer()` method, which can function as
+a [class method][classmethod]:
+
+ >>> reconst_data = example.sample_array.frompointer(vector.data)
+ >>> reconst_data[n-1]
+ 7.0
+
+Or as a single line:
+
+ >>> example.sample_array.frompointer(vector.data)[n-1]
+ 7.0
+
+I chose the somewhat awkward name of `reconst_data` for the
+reconstitued data, because if you use `data`, you clobber the earlier
+`example.sample_array(n)` definition. After the clobber, Python
+garbage collects the old `data`, and becase the old data claims it
+owns the underlying memory, Python frees the memory. This leaves
+`vector.data` and `reconst_data` pointing to unallocated memory, which
+is probably not what you want. If keeping references to the original
+objects (like I did above with `data`) is too annoying, you have to
+manually tweak the [ownership flag][ownership]:
+
+ >>> data.thisown
+ True
+ >>> data.thisown = False
+ >>> data = example.sample_array.frompointer(vector.data)
+ >>> data[n-1]
+ 7.0
+
+This way, when `data` is clobbered, SWIG doesn't release the
+underlying array (because `data` no longer claims to own the array).
+However, `vector` doesn't own the array either, so you'll have to
+remember to reattach the array to somthing that will clean it up
+before vector goes out of scope to avoid leaking memory:
+
+ >>> data.thisown = True
+ >>> del vector, data
+
+For deeply nested structures, this can be annoying, but it will work.
+
+<!-- test these doctests from the SWIG directory with
+ LD_LIBRARY_PATH=. nosetests --with-doctest --doctest-extension=mdwn ../SWIG.mdwn
+ -->
+
+[[!tag tags/tools]]
+[[!tag tags/C]]
+[[!tag tags/Python]]
+
+[SWIG]: http://www.swig.org/
+[`carrays.i`]: http://www.swig.org/Doc2.0/Library.html#Library_carrays
+[docs]: http://www.swig.org/Doc2.0/Library.html#Library_carrays
+[comedilib-patch]: http://comedi.org/git?p=comedi/comedilib.git;a=blobdiff;f=swig/comedi.i;h=5da6160d91d206d007e20c9ac5091d1735afdd30;hp=581997542927fd31cd2e0d03c220377774cfa600;hb=3fe8e6baac051d80906c6fac6c18c04c8df9ce4a;hpb=880074831499ba68c17a1c2653d71d6eef3b9cfb
+[classmethod]: http://docs.python.org/library/functions.html#classmethod
+[ownership]: http://www.swig.org/Doc2.0/Python.html#Python_nn30
--- /dev/null
+#!/usr/bin/env python
+
+import example
+
+n = 3
+
+print('double allocation and initialization in Python')
+data = example.double_array(n)
+for i in range(0,n):
+ data[i] = i**2
+
+print('passing doubles to C')
+example.print_array(n, data)
+example.print_pointer(n, data)
+vector = example.double_vector_t()
+vector.n = n
+vector.data = data
+example.print_vector(vector)
+
+print('accessing doubles from Python')
+print(data[n-1])
+try:
+ print(vector.data[n-1])
+except TypeError, e:
+ # 'SwigPyObject' object is not subscriptable
+ print e
+print(example.double_array.frompointer(vector.data)[n-1])
+
+print('sample_t allocation and initialization in Python')
+data = example.sample_array(n)
+for i in range(0,n):
+ data[i] = 2*i
+
+print('passing sample_ts to C')
+try:
+ example.print_sample_pointer(n, data)
+except TypeError, e:
+ # in method 'print_sample_pointer', argument 2 of type 'sample_t *'
+ print(e)
+try:
+ example.print_sample_array(n, data)
+except TypeError, e:
+ # in method 'print_sample_array', argument 2 of type 'sample_t []'
+ print e
+
+# the .cast() method lets you pass sample_t data to the funtion
+example.print_sample_pointer(n, data.cast())
+example.print_sample_array(n, data.cast())
+
+vector = example.sample_vector_t()
+vector.n = n
+try:
+ vector.data = data
+except TypeError, e:
+ # in method 'sample_vector_t_data_set', argument 2 of type 'sample_t *'
+ print e
+vector.data = data.cast()
+example.print_sample_vector(vector)
+
+print('accessing sample_ts from Python')
+print(data[n-1])
+try:
+ print(vector.data[n-1])
+except TypeError, e:
+ # 'SwigPyObject' object is not subscriptable
+ print e
+print(example.sample_array.frompointer(vector.data)[n-1])