SWIG: Wrapped C++ exception yields None in Python - python

I wrapped a C++ and a custom execption class as described in the answer to question: How do I propagate C++ exceptions to Python in a SWIG wrapper library?
Using my class in Python, calling the function which throws the exception and catching the exception yields the following error:
Traceback (most recent call last):
File "../WrapperTester/src/main.py", line 17, in <module>
ret = cow.milkCow()
File "..\WrapperTester\src\CowLib\CowLib.py", line 115, in milkCow
return _CowLib.Cow_milkCow(self)
src.CowLib.CowLib.CowException: None
This is my C++ class header including the exception:
class CowException {
private:
std::string message = "";
public:
CowException(std::string msg);
~CowException() {};
std::string what();
};
class _Cow
{
private:
int milk;
int hunger;
public:
_Cow();
~_Cow();
int milkCow() throw(CowException);
void feed(int food) throw(CowException);
};
And my SWIG header:
%module CowLib
%include "exception.i"
%include "Cow.i"
%{
#define SWIG_FILE_WITH_INIT
#include "Cow.h"
#include "_Cow.h"
static PyObject* pCowException;
%}
%init %{
pCowException = PyErr_NewException("_CowLib.CowException", NULL, NULL);
Py_INCREF(pCowException);
PyModule_AddObject(m, "CowException", pCowException);
%}
%exception Cow::milkCow {
try {
$action
} catch (CowException &e) {
PyErr_SetString(pCowException, e.what().c_str());
SWIG_fail;
}
}
%exception Cow::feed {
try {
$action
} catch (CowException &e) {
PyErr_SetString(pCowException, e.what().c_str());
SWIG_fail;
}
}
%include "_Cow.h"
%pythoncode %{
CowException = _CowLib.CowException
%}
Where the Header "Cow.i" is the "standard" SWIG-Header:
%module Cow
%{
#include "_Cow.h"
#include "Cow.h"
%}
%include "Cow.h"

Related

How to make it so that user-made exceptions in C++ inherit from BaseException class of Python using SWIG

I have a library in C++ where I have my own exception class called "Exception" and I use SWIG to create a wrapper for Python. The idea is that the exception is catched and its methods can be used like in this example:
example.py:
try:
functionThatThrowsError()
except myLibrary.Exception as exc:
print (exc.code(), exc.msg())
The wrapper for the exception is created without problem, the thing is that Python requires that user-made exceptions inherit from the Python builtin BaseException class and inspecting myLibrary.py generated it shows that the class is not inheriting from BaseException but from object. If I manually change that line to inherit from BaseException instead of object the example.py works perfectly.
My question is how to tell SWIG that my Exception class should inherit from the BaseException class from Python.
Here are the other files to give more context:
exception.h:
#ifndef EXCEPTION
#define EXCEPTION
#include <string.h>
#include<exception>
using namespace std;
class Exception : public exception {
public:
Exception(int c, const char* m);
const char* msg();
const int code();
virtual const char* what() const throw();
~Exception() {};
int _code; //!< Error code.
char _msg[256]; //! Error message.
};
#endif
exception.cpp:
#include "../include/Exception.h"
Exception::Exception(int c, const char* m)
{
_code = c;
strncpy(_msg, m, 256);
}
const char* Exception::msg()
{
return _msg;
}
const int Exception::code()
{
return _code;
}
const char* Exception::what() const throw()
{
return _msg;
}
myLibrary.i:
%module myLibrary
%{
#define SWIG_FILE_WITH_INIT
#include "../include/Exception.h"
%}
%include "stl.i"
%exception {
try {
$action
} catch (Exception &e) {
SWIG_Python_Raise(SWIG_NewPointerObj(
(new Exception(e.code(), e.msg())),
SWIGTYPE_p_Exception,SWIG_POINTER_OWN),
"Exception", SWIGTYPE_p_Exception);
SWIG_fail;
} catch (...) {
SWIG_exception(SWIG_RuntimeError, "unknown exception");
}
}
%include "../include/Exception.h"
%inline %{
// The -builtin SWIG option results in SWIGPYTHON_BUILTIN being defined
#ifdef SWIGPYTHON_BUILTIN
bool is_python_builtin() { return true; }
#else
bool is_python_builtin() { return false; }
#endif
%}

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
if this typemap(out) approach is correct (also in respect of memory and error handling).
The conversion in set_s_workaround() is also just a workaround. I don't think that for gnucash the python code ever needs to set this value but for completeness sake it would be nice to also have a typemap(in) std::string&& and
get rid of get_s_workaround
get init_s_2 working.
/* 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!
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"
init_s2() would work like this:
void
struct1::init_s_2()
{
std::string msg {"Couldn't find "};
set_s( msg + "");
}
credits to S. Holtermann!
%typemap(out) std::string&& {
std::string s = *$1;
$result = SWIG_From_std_string(s);
}
can be shortened to
%typemap(out) std::string&& {
$result = SWIG_From_std_string(*$1);
}
without creating a local var (credits to S. Holtermann!)
A possible typemap(in) could be
%typemap(in) std::string && (std::string *temp){
int res = SWIG_AsPtr_std_string($input, &temp);
$1 = temp;
}
works
hasn't been tested extensively
does it cause memory leaks?

How to solve the warning "Warning 401: Nothing known about base class 'xxxx' Ignored." in swig

I am trying to use swig to call c++ functions from python. Its actually working (I can import the module in python), but there are some warnings that i want to solve before i get in trouble later.
I tried to just include the code, that produces the warning.
I have this Classes in my MDF4.h Header File:
// MDF4.h
.
.
.
typedef struct
{
// Block Header
enum { RID=M4ID_FH };
// enumeration of links
enum
{
fh_fh_next, // Link to next FHBLOCK (can be NIL if list finished)
fh_md_comment,
LinkMax // # of known links
};
// Data members
M_DATE fh_time;
M_UINT8 fh_reserved[3]; // Reserved
} m4FHRecord;
.
.
template<class R,class T=BYTE,int ID=R::RID> class m4BlockImpl : public m4Block,public R
{
public:
m4BlockImpl(size_t nVar=0) : m4Block(R::LinkMax),m_var(nVar)
{
m_Hdr.hdr_hdr=M4ID_ID;
m_Hdr.hdr_id=ID;
R *pThis=static_cast<R *>(this);
memset(pThis,0,sizeof(R));
}
BOOL setCommentBlk(m4Block &TXorMD,int linkNo)
{
// cannot call twice
if (!hasLink(linkNo))
{
ATLASSERT(TXorMD.hdrID()==M4ID_TX || TXorMD.hdrID()==M4ID_MD);
ATLASSERT(m_File);
M_LINK mdAt=TXorMD.Create(m_File,3);
if (mdAt)
{
setLink(linkNo,mdAt);
return TRUE;
}
}
return FALSE;
}
};
.
.
class M4FHBlock : public m4BlockImpl<m4FHRecord>
{
public:
M4FHBlock(MDF4File *File); // ctor: create and insrt current time
M4FHBlock(); // used for reading
BOOL setComment(m4Block &md); // CANNOT be a TX Block!
DECLARE_DUMP
};
And this is my MDF4.i interface file:
%module MDF4
%{
#include "mdf4_lib_v2_019\\stdafx.h"
#include "mdf4_lib_v2_019\\utf8.h"
#include "mdf4_lib_v2_019\\Resource.h"
#include "mdf4_lib_v2_019\\mdfConfig.h"
#include "mdf4_lib_v2_019\\Ptrlist.h"
#include "mdf4_lib_v2_019\\dynArray.h"
#include "mdf4_lib_v2_019\\miniz.c"
#include "mdf4_lib_v2_019\\md5.h"
#include "mdf4_lib_v2_019\\m4Dump.h"
#include "mdf4_lib_v2_019\\mdFile.h"
#include "mdf4_lib_v2_019\\mdfTypes.h"
#include "mdf4_lib_v2_019\\mdf4.h"
%}
%feature("autodoc", "1");
%feature("flatnested");
%rename(__incr__) Indent::operator++;
%rename(__incr__) ptrlist::iterator::operator++;
%rename(__eq__) ptrlist::iterator::operator=;
%rename(__decr__) ptrlist::iterator::operator--;
%rename(__eq__) ptrlist::iterator::operator=;
%rename(__invert__) ptrlist::iterator::operator!;
%rename(__incr__) DbtObjPtrList::iterator::operator++;
%rename(__eq__) DbtObjPtrList::iterator::operator=;
%rename(__decr__) DbtObjPtrList::iterator::operator--;
%rename(__eq__) DbtObjPtrList::iterator::operator=;
%rename(__invert__) DbtObjPtrList::iterator::operator!;
%include <wchar.i>
%include <cwstring.i>
%include <std_vector.i>
%include <std_map.i>
%include "mdf4_lib_v2_019\\stdafx.h"
%include "mdf4_lib_v2_019\\utf8.h"
%include "mdf4_lib_v2_019\\Resource.h"
%include "mdf4_lib_v2_019\\mdfConfig.h"
%include "mdf4_lib_v2_019\\Ptrlist.h"
%include "mdf4_lib_v2_019\\dynArray.h"
%include "mdf4_lib_v2_019\\miniz.c"
%include "mdf4_lib_v2_019\\md5.h"
%include "mdf4_lib_v2_019\\m4Dump.h"
%include "mdf4_lib_v2_019\\mdFile.h"
%include "mdf4_lib_v2_019\\mdfTypes.h"
%include "mdf4_lib_v2_019\\mdf4.h"
But i get always this warning after calling swig -c++ -python MDF4.i:
mdf4_lib_v2_019\mdf4.h(1226) : Warning 401: Nothing known about base class 'm4BlockImpl< m4FHRecord >'. Ignored.
mdf4_lib_v2_019\mdf4.h(1226) : Warning 401: Maybe you forgot to instantiate 'm4BlockImpl< m4FHRecord >' using %template.
---- EDIT -----
I created a simple example with the same warning output:
test.h:
typedef struct
{
enum { RID=1};
}myStruct;
template<class A,class B=BYTE,int ID=A::RID> class Foo : public A
{
int a;
public:
Foo()
{
int a = 1;
}
};
class Bar : public Foo<myStruct>
{
public:
Bar();
};
test.i:
%module test
%{
#include "test.h"
%}
%include "test.h"

Passing a C++ class instance into a C++ class on boost.python C++

I'm actually trying to use a C++ class like an attribute of an other C++ class in a Boost python library. I want to bind this two class in python like this :
import ClassParameter
t = ClassParameter.Slave("hi")
a = ClassParameter.Master()
a.setSlave(t)
Then i'd like to use some method from my Master class that use an instance of my Slave class : a.setSlave(t)
Problem is, when I bind the Slave, the Master got only a copy of the instance. And I don't know how I can do the link between both.
This is my boost code :
#include <boost/python.hpp>
#include "Master.h"
#include "Slave.h"
BOOST_PYTHON_MODULE(ClassParameter)
{
using namespace boost::python;
class_<Slave>("Slave")
.def(init<>())
.def(init<std::string>())
.def("getMessage", &Slave::getMessage)
.def("setMessage", &Slave::setMessage);
class_<Master>("Master")
.def(init<>())
.def(init<Slave>())
.def("showMessage",&Master::showMessage)
.def("setSlave",&Master::setSlave)
.def("getSlave",&Master::getSlave);
}
This is my class Slave :
#ifndef _SLAVE_H_
#define _SLAVE_H_
#include <iostream>
class Slave
{
public:
Slave(std::string message);
Slave();
Slave(const Slave& s1 );
std::string getMessage(void) ;
void setMessage(std::string message);
private:
std::string m_message;
};
#endif //_SLAVE_H_
Slave::Slave(){
m_message = "no message";
}
Slave::Slave(std::string message){
m_message = message;
}
Slave::Slave(const Slave& s1 ){
m_message = s1.m_message;
}
std::string Slave::getMessage(void) {
return m_message;
}
void Slave::setMessage(std::string message){
m_message = message;
}
This is my Master Class:
#ifndef _MASTER_H_
#define _MASTER_H_
#include "Slave.h"
#include <iostream>
class Master
{
public:
Master();
Master(Slave s) : m_slave(&s) {}
void showMessage();
void setSlave(Slave s);
Slave getSlave();
private:
Slave *m_slave;
};
#endif //_MASTER_H_
#include "Master.h"
Master::Master(){
m_slave =0;
}
void Master::showMessage(){
if (m_slave!=0) std::cout<< ( m_slave->getMessage() )<<std::endl;
}
void Master::setSlave(Slave s){
m_slave = &s;
}
Slave Master::getSlave(){
return *m_slave;
}
The error message when i'm using showMessage from Master is :
Traceback (most recent call last):
File "test.py", line 13, in <module>
a.showMessage()
RuntimeError: basic_string::_M_construct null not valid
I think it's because the setSlave got a temp copy, but i don't know an other way to solve this problem.

Wrapp C++ library with SWIG with overloading called functions

I have compiled SWIG python wrapper for the following function:
mylib.h:
#pragma once
#include <string>
myEnum {foo=0, bar};
myEnum getResult(const std::string& path, std::string& result);
And interface:
%module mylib
%include <std_string.i>
%{
#include "mylib.h"
%}
%apply const std::string & {std::string &};
%apply std::string & {std::string &};
int getResult(const std::string& path, std::string& result);
But I have a problem with function getResult. Because in Python strings are immutable . So I interested can I overload my function in interface file?
For example something like this:
%module mylib
%include <std_string.i>
%{
#include "mylib.h"
%}
%apply const std::string & {std::string &};
%apply std::string & {std::string &};
std::string getResult(std::string& path)
{
std::string result;
getResult(path, result);
return result;
}

Categories