1

I make some trials with swig in order to extend basic C++ class to python. I found a behavior related to the use of sets that I can't explain so far. Here are my scripts:

MyClass.h:

#pragma once
#include <set>
#include <vector>

class MyClass
{
public:
    MyClass();
    void setSTLVector(const std::vector<int> &vector);
    void setSTLSet(const std::set<int> &set);

private:
    std::vector<int> _stlVector;
    std::set<int> _stlSet;
};

MyClass.cpp:

#include "MyClass.h"

MyClass::MyClass()
{
}

void MyClass::setSTLVector(const std::vector<int> &vector)
{
    _stlVector = vector;
}

void MyClass::setSTLSet(const std::set<int> &set)
{
    _stlSet = set;
}

MyClass.i:

%module MyClass

%{
    #include "MyClass.h"
%}

%include <typemaps.i>

%include "std_vector.i"
%template(IntVector) std::vector<int>;

%include "std_set.i"
%template(IntSet) std::set<int>;

%include "MyClass.h"

when compiling everything is (seems) OK. My misunderstanding begins when running my extension into python. Indeed:

In [1]: import MyClass
In [2]: cls = MyClass.MyClass()
In [3]: cls.setSTLVector([1,2,3,4])

works perfectly at least how I expect i.e. the python list of integers is casted internally to a std::vector<int>. For the set:

In [1]: import MyClass
In [2]: cls = MyClass.MyClass()
In [3]: cls.setSTLVector({1,2,3,4})

triggers the following error:

TypeError: in method 'MyClass_setSTLSet', argument 2 of type 'std::set< int,std::less< int >,std::allocator< int > > const &'

This error being probably related to another I have when I declare a set using the type I defined in swig:

In [1]: import MyClass
In [2]: cls = MyClass.IntSet({1,2,3,4})

which gives:

NotImplementedError: Wrong number or type of arguments for overloaded function 'new_IntSet'.
  Possible C/C++ prototypes are:
    std::set< int >::set(std::less< int > const &)
    std::set< int >::set()
    std::set< int >::set(std::set< int > const &)

Would you have any idea about what I am doing wrong or is that a normal behavior ?

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
Eurydice
  • 8,001
  • 4
  • 24
  • 37

1 Answers1

1

The typemaps for std_set.i unintuitively expect a Python list as input and not a set.

>>> import MyClass
>>> cls = MyClass.MyClass()
>>> cls.setSTLVector([1,2,3,4]) # works
>>> cls.setSTLSet([1,2,3,4])    # works
>>> cls.setSTLSet({1,2,3,4})    # doesn't work
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\MyClass.py", line 385, in setSTLSet
    return _MyClass.MyClass_setSTLSet(self, set)
TypeError: in method 'MyClass_setSTLSet', argument 2 of type 'std::set< int,std::less< int >,std::allocator< int > > const &'**strong text**

You would have to define your own custom typemap to take a set as input.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • 1
    OK i understand. Thanks for your help. – Eurydice Mar 23 '20 at 17:47
  • @Mark Tolonen is there a simple way to define such a typemap? – Fractalic Mar 19 '21 at 18:31
  • 1
    @Fractalic It's a few lines of code for a specific input typemap for `std::set`, similar to [this answer](https://stackoverflow.com/a/23025297/235698) but use the [Python C API for a `set`](https://docs.python.org/3/c-api/set.html) and populate a `std::set` instead. Give it a try and ask a SO question if you get stuck. I've never tried making a complete template implementation. It's doable but look at the `std_set.i` implementation if you want to do that. – Mark Tolonen Mar 19 '21 at 19:04