Passing data between Python and C++ by SWIG

Passing data between Python and C++ by SWIG

Example - Pass dict from Python to C++

This example uses only one .i file, and the function is implemented inside the .i file.

Codes

// File: dictTest.i
%module dictTest

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

namespace std {
    %template(Str2StrMap) 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;
    }
}
%}

Compile

The following cmd creates dictTest.py, dictTest_wrap.cxx

swig  -python  -c++  dictTest.i

Compile and link to _dictTest.so

g++  -fPIC  -shared  -undefined dynamic_lookup -I/usr/local/anaconda3/envs/django/include/python3.6m -L/usr/local/anaconda3/envs/django/lib  -o _dictTest.so  dictTest_wrap.cxx

Python test

import dictTest

m = {'a' : 'b', 'c' : 'd'}
dictTest.foo(m)

# do dict to map converting first, not necessary
x = dictTest.Str2StrMap(m)
dictTest.foo(x)

Reference

https://stackoverflow.com/questions/9041192/how-does-swig-wrap-a-mapstring-string-in-python

Example - Pass {(str, str): number} from Python to C++

Codes

// File: dictTest2.i
%module dictTest2

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

namespace std {
    %template(Str2FloatMap) map<string, double>;
	
    // Tuple / pair as key of dict / map
    %template(StrPair2FloatMap) map<pair<string, string>, double>;
    
    // %template(IntPair2IntMap) map<pair<int, int>, int>;
}

void tryStr2Float(const std::map<std::string, double> &val);
void tryStrPair2Float(const std::map<std::pair<std::string, std::string>, double> &val);
// void tryIntPair2Int(const std::map<std::pair<int, int>, int> &val);

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

using namespace std;

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

void
tryStrPair2Float(const map<pair<string, string>, double> &val) {
    map<pair<string, string>, double>::const_iterator i = val.begin();
    map<pair<string, string>, double>::const_iterator end = val.end();
    while (i != end) {
        cout << "(" << (i->first).first << "," << (i->first).second <<  ") : " << i->second << endl;
        i++;
    }
}
%}

Compile

swig  -python  -c++  dictTest2.i
$ g++  -fPIC  -shared  -undefined dynamic_lookup  -I/usr/local/anaconda3/envs/django/include/python3.6m  -L/usr/local/anaconda3/envs/django/lib  -o _dictTest2.so  dictTest2_wrap.cxx

Test

import dictTest2

m1 = {k:k * 0.5 for k in range(5)}
dictTest2.tryStr2Float(m1)

m2 = {('x{}'.format(i), 'y{}'.format(i)): i for i in range(5)}
dictTest2.tryStrPair2Float(m2)

Example - dict in, dict out

Ref: https://stackoverflow.com/questions/38556223/returning-arguments-in-swig-python

Codes

Header file

// File: mapIO.h
#pragma once

#include <iostream>
#include <string>
#include <unordered_map>

void printThenAdd(std::unordered_map<std::string, std::string>* inout) {
    for( const auto& n : (*inout) ) {
        std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
    }
    (*inout)["BLACK"] = "#000000";
};

Interface file

// File : mapIO.i
%module mapIO

%{
  #include "mapIO.h"
%}

%include "typemaps.i"

%typemap(in) std::unordered_map<std::string, std::string>* (std::unordered_map<std::string, std::string> temp) {
    PyObject *key, *value;
    Py_ssize_t pos = 0;

    $1 = &temp;
    temp = std::unordered_map<std::string, std::string>();

    while (PyDict_Next($input, &pos, &key, &value)) {
        (*$1)[PyUnicode_AsUTF8(key)] = std::string(PyUnicode_AsUTF8(value));
    }
}

%typemap(argout) std::unordered_map<std::string, std::string>* {
    $result = PyDict_New();
    for(const auto& n : *$1) {
        PyDict_SetItemString(
            $result, 
            n.first.c_str(),
            PyString_FromString(n.second.c_str())
        );
    }
}

%include "mapIO.h"

Compile

Create mapIO_wrap.cxx

swig  -python  -c++  mapIO.i

Compile and link to _mapIO.so

g++  -fPIC  -shared  -undefined dynamic_lookup -I/usr/local/anaconda3/envs/django/include/python3.6m -L/usr/local/anaconda3/envs/django/lib  -o _mapIO.so  mapIO_wrap.cxx

Test

import mapIO
m = {'WHITE' : '#FFFFFF'}
a = mapIO.printThenAdd(m)

Output is an updated dictionary

In[2]: a
Out[3] : {'BLACK': '#000000', 'WHITE': '#FFFFFF'}

Example - list in, tuple out

Codes

// File: arrayTest.i
%module arrayTest

%include "std_vector.i"
%include "std_string.i"

// Tell SWIG about the templates you will use.
%template() std::vector<double>;  // no need to give name
%template() std::vector<int>;

// Declaration
template<typename T> std::vector<T> revert(const std::vector<T> &val);

// Generate wrappers from function template
%template(revertDouble) revert<double>;
%template(revertInt)    revert<int>;

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

    using namespace std;

    template<typename T> vector<T> revert(const vector<T> &val) {
        vector<T> result;
        for (int i=val.size()-1; i>=0; i--) {
            result.push_back(val[i]);
            // cout << i << " : " << val[i] << endl;
        }
        return result;
    }
%}

Compile

Create arrayTest_wrap.cxx

swig  -python  -c++  arrayTest.i

Compile and link to _arrayTest.so

g++  -fPIC  -shared  -undefined dynamic_lookup -I/usr/local/anaconda3/envs/django/include/python3.6m -L/usr/local/anaconda3/envs/django/lib  -o _arrayTest.so  arrayTest_wrap.cxx

Python test

import arrayTest

a = range(10)
b = arrayTest.revert(a)
print(b)
type(b)

Reference

Table of Contents