I'm working on python wrapper for some C++ code. In order to generate such wrapper SWIG tool is used.
Almost all code is wrapped perfectly and works as expected except one thing. C++ code contains function which returns smart_ptr to structure which is derived from base structure. Base structure contains map to store key-value pairs and getattr , setattr functions to get value from that map by key and set value for the specified key. The function mentioned above fills out map by the set of keys depending on parameter. Let's say that the value for each key is a string containing one space for simplicity.
The idea behind all that code is to call the function from python code, and be able to fill out returned map with values using setattr for example.
I implemented SWIG interface file, built python module but python code is crashed on attempt to call function.
I read some SWIG related questions here and tried several proposals from the answers. But I still was not able to get working python wrapper.
If I delete setattr and getattr functions from both C++ code and SWIG interface then python module compiles, function can be called but obviously I can't store the values in map.
Also I tried to use %shared_ptr(TableRequest) instead of struct table_request_ptr_t in the interface file. In this case I got SWIG object as returned value and can't set or get attributes of TableRequest. str and repr do not work also as they are implemented in C++ code in this case.
Can somebody help me to fix this issue? E.g. propose how to fix setattr and getattr or how to wrap shared_ptr correctly.
Below you can find a sample of C++ source code, SWIG interface file and error message. Also you can find the whole CMake project here - https://www.filehosting.org/file/details/817351/swig_sample.zip
The following line can be used to generate make file: cmake ../ -DPYTHON_LIBRARY={your path to python libs} -DPYTHON_INCLUDE_PATH={your path to python headers}
The following SW was used to build python module which wraps C++: - SWIG 4.0.0 - gcc 7.3.1 - python 3.5 - RHEL 7
(Also I tried to build python module with the same result on Xubuntu 16.04 with the same result. gcc was 7.1 but I don't think it may cause any differencies.)
connect.hpp
#ifndef CONNECT_HPP
#define CONNECT_HPP
#include <string>
#include <Python.h>
#include "request.hpp"
class Connection {
private:
std::string _conn_params;
public:
explicit Connection();
explicit Connection( const std::string& options );
~Connection();
void disconnect();
table_request_ptr_t GetTableRequest( const char* request_id );
};
#endif // CONNECT_HPP
connect.cpp
#include "connect.hpp"
Connection::Connection(){
_conn_params = "tty";
}
Connection::Connection( const std::string& options ){
_conn_params = options;
}
Connection::~Connection() {
disconnect();
}
void Connection::disconnect() {
std::cout << "Disconnected" << std::endl;
}
table_request_ptr_t Connection::GetTableRequest( const char* request_id ) {
return table_request_ptr_t( new TableRequest() );
}
request.hpp
#ifndef REQUEST_HPP
#define REQUEST_HPP
#include <Python.h>
#include <string>
#include <map>
#include <iostream>
#include <memory>
struct Request {
typedef std::map< std::string, std::string > attribs_t;
attribs_t _attribs;
std::string _buffer;
inline Request() { reset(); };
void reset();
inline int __len__() {
return _buffer.length();
}
int __setattr__( const std::string &key, PyObject *py_value );
PyObject * __getattr__( const std::string &key );
const char* __str__();
std::string __repr__();
};
struct TableRequest : public Request {
const std::string itable;
inline TableRequest() {}
};
typedef std::shared_ptr< TableRequest > table_request_ptr_t;
#endif //REQUEST_HPP
request.cpp
#include <math.h>
#include "pyobject_ptr.hpp"
#include "request.hpp"
void Request::reset(){
_attribs[ "name" ] = " ";
_attribs[ "description" ] = " ";
_attribs[ "status" ] = " ";
}
int Request::__setattr__( const std::string &key, PyObject *py_value ) {
pyobject_ptr obj( py_value );
std::string tmp;
if ( PyUnicode_Check( py_value ) ){
_attribs[ key ] = PyUnicode_AsUTF8( py_value );
}
return 0;
}
PyObject * Request::__getattr__( const std::string &key ) {
PyObject* val = PyUnicode_FromString( _attribs[ key ].c_str() );
if ( val ) {
Py_INCREF( val );
return val;
} else {
Py_RETURN_NONE;
}
}
const char* Request::__str__() {
return _buffer.c_str();
}
std::string Request::__repr__() {
std::string repr = "[ ";
for( const auto& i: _attribs ) {
repr += i.first + " : " + i.second;
}
repr += " ]";
return repr;
}
pyconnect.i
%module pyconnect
%{
#include "connect.hpp"
%}
struct table_request_ptr_t {
TableRequest* operator -> () {
return get();
}
};
struct TableRequest {
const std::string itable;
void reset();
int __len__();
int __setattr__(const std::string &key, PyObject *py_value) throw( std::exception );
PyObject * __getattr__(const std::string &key) throw( std::exception );
const char* __str__();
std::string __repr__();
};
class Connection {
public:
Connection() throw( std::exception );
Connection( const std::string& options ) throw( std::exception );
~Connection();
void Connection::disconnect() throw( std::exception );
table_request_ptr_t GetTableRequest( const char* request_id ) throw( std::exception );
};
Python code which causes error
import pyconnect
conn = pyconnect.Connection()
req = conn.GetTableRequest("Something")
Error message is the following:
TypeError: in method 'table_request_ptr_t___setattr__', argument 1 of type 'table_request_ptr_t *'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/sd/pyconnect/build/pyconnect.py", line 166, in GetTableRequest
return _pyconnect.Connection_GetTableRequest(self, request_id)
SystemError: <built-in function Connection_GetTableRequest> returned a result with an error set