4

I have searched on the web and didn't get success. I'm wrapping the sample code below to Python (using SWIG):

class atomo {
public:
    int i;
    atomo(int a) {
        i = a;
    };      
};

class funa {
public:
    atomo *lista[3];

    funa() {
        lista[0] = new atomo(1);
        lista[1] = new atomo(2);
        lista[2] = new atomo(3);
    };
};

But Python can't iterate over or access lista using the comands

>>> test = myModule.funa()
>>> test.lista[0]
      Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in __iter__
      TypeError: 'SwigPyObject' object is not subscriptable

>>> for i in test.lista:
>>>     print(i)
      Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in __iter__
      TypeError: 'SwigPyObject' object is not subscriptable

How can I make lista iterable? There is a way to use Python lists instead of C++ arrays?

My Python version is 3.2 and I'm using SWIG 2.0.4 with g++ 4.6.1

Thanks

Caio S. Souza
  • 141
  • 3
  • 11

3 Answers3

3

It's a little unclear from your question if you want to use std::vector or an array of your own types.

For std::vector, given some C++ like:

#include <vector>
#include <string>

struct foo {
  std::string name;
};

inline std::vector<foo> test() {
  std::vector<foo> ret;
  foo instance;
  instance.name = "one";
  ret.push_back(instance);
  instance.name = "two";
  ret.push_back(instance);
  return ret;
}

You can wrap it with %template, pyabc.i and std_vector.i e.g.:

%module test

%{
#include "test.h"
%}

%include "pyabc.i"
%include "std_vector.i"

%include "test.h"

%template (FooVector) std::vector<foo>;

which will behave intuitively on the Python type. You'll need to call SWIG with something like:

swig -python -c++ -py3 -extranative test.i

If the idea is to wrap a "custom" container to behave intuitively on the Python side I gave a detailed example in a previous answer.

Community
  • 1
  • 1
Flexo
  • 87,323
  • 22
  • 191
  • 272
1

You might want to solve this on the Python side instead of the C++/SWIG side for simplicity.

# wrapper/facade
class Funa:
    def __init__(self):
        self._impl = myModule.funa()   # _impl => implementation

    def __iter__(self):
        for i in xrange(3):
            yield self._impl.lista[i]

test = Funa()
for x in test:
    print(x)
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • @user1154091: As long as you keep the inner loops in Python, you won't gain too much speed. Move the inner loops to the C++ code to really make a difference. – Sven Marnach Jan 17 '12 at 15:20
  • @Sven Marnach: This is just a simple piece of code that shows the same error. The bottleneck of my real code is the instanciation of the class `atomo` – Caio S. Souza Jan 17 '12 at 15:30
  • @CaioS.: My comment above was in response to your (now deleted) comment that you don't like this answer since it might be too slow. – Sven Marnach Jan 17 '12 at 15:41
0

A similar approach to larsmans is to have Funa.__iter__ return a generator object. Then you would only need to add to the interface SWIG creates. (With his wrapping, you would have to wrap every other method, or play with __getattr__.) Roughly it would be like this

class Funa:

  class FunaIter :
    def __init__(self, parent) :
      self.parent = parent
      self.pos = 0

    def __iter__(self) :
      while self.pos < 3 :
        yield self.parent.lista[self.pos]
        self.pos += 1

  def __iter__(self) :
    return self.FunaIter(self)

This should be simpler to insert into your SWIG file using the %extend and %pythoncode directives.

Also, SWIG has wrappers for STL containers, so perhaps using those, you can easily obtain the necessary item getters.

Community
  • 1
  • 1
AFoglia
  • 7,968
  • 3
  • 35
  • 51