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;
}
Related
I'm trying to wrap an unordered map in a python dictionary using swig:
// UsingAnUnorderedMap.h
#ifndef LEARNSWIG_USINGUNORDEREDMAP_H
#define LEARNSWIG_USINGUNORDEREDMAP_H
#include <unordered_map>
#include <string>
std::unordered_map<std::string, int> makeStringToIntMap(){
return std::unordered_map<std::string, int>{
{"first", 4},
{"second", 5},
};
}
#endif //LEARNSWIG_USINGUNORDEREDMAP_H
//UsingAnUnorderedMap.i
%module UsingUnorderedMap
%{
#include "UsingUnorderedMap.h"
#include <iostream>
#include <unordered_map>
%}
%include "std_string.i"
%include "std_pair.i"
%include "std_unordered_map.i"
%template(StringToIntMap) std::unordered_map<std::string,int>;
%include "UsingUnorderedMap.h"
%typemap(out) StringToIntMap {
PyDictObject* dict = PyDict_New($input);
for (auto &item: StringToIntMap){
PyDict_SetItem(dict, PyUnicode_FromString(item.first), item.second);
}
$result = dict;
}
# testUsingAnUnorderedMap.py
import sys
sys.path += [
r"D:\LearnSwig\install-msvc\UsingUnorderedMap"
]
import UsingUnorderedMap
print(type(UsingUnorderedMap.makeStringToIntMap()))
This produces
<class 'UsingUnorderedMap.StringToIntMap'>
i.e. it just ignores the typemap. Technically the StringToIntMap behaves pretty much the same as a Python dict - at far as I can tell, but I think there's confort for Python users in Python dictionaries and so it would be better if this were a straight up dictionary. Does anybody have any pointers on how to achieve this?
For convenience, you can build this code using the following CMake code. Note that I build this using the command -DSWIG_EXECUTABLE=/path/to/swig.exe.
# CMakeLists.txt
set(Python_ROOT_DIR "C:/Miniconda3")
find_package(Python COMPONENTS Interpreter Development NumPy)
message("Python_EXECUTABLE ${Python_EXECUTABLE}")
find_package(SWIG 4.0.0 REQUIRED
COMPONENTS python
)
include(UseSWIG)
set_property(SOURCE UsingUnorderedMap.i PROPERTY CPLUSPLUS ON)
swig_add_library(UsingUnorderedMap
LANGUAGE python
TYPE MODULE
SOURCES UsingUnorderedMap.i UsingUnorderedMap)
set_target_properties(UsingUnorderedMap
PROPERTIES LANGUAGE CXX)
target_include_directories(UsingUnorderedMap PUBLIC
${Python_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(UsingUnorderedMap PUBLIC ${Python_LIBRARIES})
install(TARGETS UsingUnorderedMap DESTINATION UsingUnorderedMap)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/UsingUnorderedMap.py
${CMAKE_CURRENT_SOURCE_DIR}/testUsingUnorderedMap.py
DESTINATION UsingUnorderedMap)
Here goes a small example showing conversion of std::unordered_map to a python dictionary
%module dictmap
%{
#include "test.h"
%}
%typemap(out) std::unordered_map<std::string, std::string> (PyObject* obj) %{
obj = PyDict_New();
for (const auto& n : $1) {
PyObject* strA = PyUnicode_FromString(n.first.c_str());
PyObject* strB = PyUnicode_FromString(n.second.c_str());
PyDict_SetItem(obj, strA, strB);
Py_XDECREF(strA);
Py_XDECREF(strB);
}
$result = SWIG_Python_AppendOutput($result, obj);
%}
%include "test.h"
Small inline function
#pragma once
#include <string>
#include <unordered_map>
std::unordered_map<std::string, std::string> makeStringMap() {
return std::unordered_map<std::string, std::string> {
{"first", "hello"},
{"second", "world"},
};
}
I hope that you can see what you have made wrong. You are returning an empty dictionary created using an empty $input. I am not sure of whether you need to defined a PyObject argument, but I do this always in case it is needed together with another typemap.
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?
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"
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"
I am trying to write Python wrap for C code which uses struct.
modules.c:
struct foo
{
int a;
};
struct foo bar;
modulues.i
%module nepal
%{
struct foo
{
int a;
}
%}
extern struct foo bar;
But during compiling I am given error:
In function ‘Swig_var_bar_set’:
error: ‘bar’ undeclared (first use in this function)
Could you be so kind to help me how to correctly define export struct variable ?
Try this:
%module nepal
%{
struct foo
{
int a;
};
extern struct foo bar;
%}
struct foo
{
int a;
};
extern struct foo bar;
The code in %{ %} is inserted in the wrapper, and the code below it is parsed to create the wrapper. It's easier to put this all in a header file so it is not so repetitive:
modules.h
struct foo
{
int a;
};
extern struct foo bar;
modules.c
#include "modules.h"
struct foo bar;
modules.i
%module nepal
%{
#include "modules.h"
%}
%include "modules.h"