pythonc++11swiggnucash

Python SWIG wrapper for C++ rvalue std::string &&


I'm trying to build a python wrapper for gnucash c++ parts. In QofBackend I encountered the method const std::string && get_message (). In python this message returns <Swig Object of type 'std::string *' at 0x7f4a20f5c9f0> instead of a string as in my setting as there is no swig typemap for std::string rvalue reference.

I didn't really find a simple explanation so I rebuilt an example setting and dug into c++ which I barely know. I managed to get the string into python but I'd like to know

/* example.hpp */
#include <string>

using namespace std;

struct struct1{
        string s;
        const std::string&& get_s();
        void set_s(string&&);
        void set_s_workaround(string);
        void init_s();
        void init_s_2();
        void print_s();
};

string conv_rvalue_string(string);
/* example.cpp */
#include<iostream> 
#include"example.hpp"

using namespace std; 

void
struct1::set_s (std::string&& msg)
{
     s = msg;
}

std::string
conv_rvalue_string (std::string msg)
{
        return msg;
}

void
struct1::set_s_workaround(std::string msg)
{
        set_s ( conv_rvalue_string(msg) );
}

void
struct1::init_s ()
{
     set_s("Some content");
}

void
struct1::init_s_2()
{
     std::string msg {"Couldn't find "};
     /* set_s( msg ); */
}

void
struct1::print_s ()
{
     cout<<get_s()<<endl;
}

const std::string&&
struct1::get_s ()
{
    return std::move(s);
}
/* example.i */
%module example

%include "std_string.i"

%typemap(out) std::string&& {
  std::string s = *$1;
  $result = SWIG_From_std_string(s);
}


%{
#include <string>
#include "example.hpp"
%}

%include "example.hpp"
#!/bin/bash
swig3.0 -c++ -shadow -python example.i
g++ -fpic -c example.hpp example.cpp example_wrap.cxx -I/usr/include/python3.7
g++ -shared example_wrap.o example.o -o _example.so 
# pyexample.py
import example

s1 = example.struct1()
s1.set_s_workaround('TEST')
s1.print_s()
print(s1.get_s())

Thanks for the help!


Solution

  • This std::string && typemap uses a temporary std::string that is passed to the method that takes a std::string rvalue reference.
    The fragment dependecies in the %typemap ensure that the used fragments are ready in the wrapper file. We simply use the fragments that are already provided by "std_string.i".

    /* example.i */
    %module example
    
    %include "std_string.i"
    
    %typemap(out, fragment="SWIG_From_std_string") std::string&& {
      $result = SWIG_From_std_string(*$1);
    }
    
    %typemap(in, fragment="SWIG_AsVal_std_string") std::string&& (std::string temp) {
      int res = SWIG_AsVal_std_string($input, &temp);
      $1 = &temp;
    }
    
    %{
    #include <string>
    #include "example.hpp"
    %}
    
    %include "example.hpp"