Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does SWIG wrap a map<string,string> in Python?

Tags:

c++

python

stl

swig

I'm using SWIG 2.0 to create a Python wrapper for a C++ library. One method has an argument of type "const std::map&". SWIG happily generates a wrapper for it, but I can't figure out how to invoke the method. If I pass, for example, {"a":"b"} for that argument, I get a "NotImplementedError: Wrong number or type of arguments for overloaded function" error.

I looked at the generated .cxx file in the hope it would clarify, but it didn't. Here's the code that processes that argument:

res4 = SWIG_ConvertPtr(obj3, &argp4, SWIGTYPE_p_std__mapT_std__string_std__string_t,  0  | 0);
if (!SWIG_IsOK(res4)) {
  SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "new_Context" "', argument " "4"" of type '" "std::map< std::string,std::string > const &""'"); 
}

It clearly knows that argument exists, and that it's supposed to be something that gets converted to a map. But I can't figure out what it actually wants me to pass for it.

like image 767
peastman Avatar asked Jan 27 '12 22:01

peastman


1 Answers

When you're using a C++ template (e.g. a std::map<string, string>) you need to create an alias for it in your .i file so you can use it in python:

namespace std {
%template(map_string_string) map<string, string>;
}

Now let's say you want to wrap a function that looks like this:

void foo(const std::map<string, string> &arg);

On the python side, you need to pass a map_string_string to foo, not a python dict. It turns out that you can easily convert a python dict to a map though by doing this:

map_string_string({ 'a' : 'b' })

so if you want to call foo, you need to do this:

foo(map_string_string({ 'a' : 'b' }))

Here's full example code that works.

// test.i
%module test

%include "std_string.i"
%include "std_map.i"

namespace std {
    %template(map_string_string) map<string, string>;
}

void foo(const std::map<std::string, std::string> &val);

%{
#include <iostream>
#include <string>
#include <map>

using namespace std;
void
foo(const map<string, string> &val)
{
    map<string, string>::const_iterator i = val.begin();
    map<string, string>::const_iterator end = val.end();
    while (i != end) {
        cout << i->first << " : " << i->second << endl;
        ++i;
    }
}

%}

And the python test code:

#run_test.py
import test

x = test.map_string_string({ 'a' : 'b', 'c' : 'd' })
test.foo(x)

And my command line:

% swig -python -c++ test.i
% g++ -fPIC -shared -I/usr/include/python2.7  -o _test.so test_wrap.cxx
% python run_test.py
a : b
c : d
like image 99
Nathan Binkert Avatar answered Oct 15 '22 23:10

Nathan Binkert