Swig - wrapping C struct - python

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"

Related

pybind11: inherit/import one module into another

I have 2 pybind11 modules, Foo and Bar
I would like to import all symbols from Bar into Foo
eg, in Foo:
from Bar import *
My first attempt was to create 2 modules (using PYBIND11_MODULE), and call pybind11::module::import("bar") inside the module definition of foo:
#include <pybind11/pybind11.h>
namespace py = pybind11;
struct Bar
{
void bar()
{
py::print("in bar");
}
};
struct Foo
{
void foo()
{
py::print("in foo");
}
};
PYBIND11_MODULE(bar, m)
{
py::class_<Bar>(m, "Bar")
.def(py::init<>())
.def("bar", &Bar::bar);
}
PYBIND11_MODULE(foo, m)
{
py::module::import("bar"); // attempt to import Bar into Foo
py::class_<Foo>(m, "Foo")
.def(py::init<>())
.def("foo", &Foo::foo);
}
However, when importing the foo module in python, I get an error:
ModuleNotFoundError: No module named 'bar'
The only reason I want to do this is to be able to separate the definitions of various different types into separate files, and then "collect" them together into a single module.
My 2nd attempt was to create helper functions which take the module instance as a parameter, and then instantiates the relevant pybind11::class_ with the provided module instance
Eg:
in foo.h:
#pragma once
#include <pybind11/pybind11.h>
namespace py = pybind11;
struct Foo
{
void foo()
{
py::print("in foo");
}
};
void PyFoo(pybind11::module_& m)
{
py::class_<Foo>(m, "Foo")
.def(py::init<>())
.def("foo", &Foo::foo);
};
in bar.h:
#pragma once
#include <pybind11/pybind11.h>
namespace py = pybind11;
struct Bar
{
void bar()
{
py::print("in bar");
}
};
void PyBar(pybind11::module_& m)
{
py::class_<Bar>(m, "Bar")
.def(py::init<>())
.def("bar", &Bar::bar);
};
in module.cpp:
#include "foo.h"
#include "bar.h"
#include <pybind11/pybind11.h>
namespace py = pybind11;
PYBIND11_MODULE(foo, m)
{
PyBar(m);
PyFoo(m);
}
This actually works!
However, having to create a helper function for every single type I want to register with pybind11 seems a little boiler-platey.
Is there a better way to "collect" multiple pybind11 type definitions into a single module?

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"

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;
}

typedef does not work with SWIG (python wrapping C++ code)

Hello and thanks for your help in advance !
I am writing a python wrapper (SWIG 2.0 + Python 2.7) for a C++ code. The C++ code has typedef which I need to access in python wrapper. Unfortunately, I am getting following error when executing my Python code:
tag = CNInt32(0)
NameError: global name 'CNInt32' is not defined
I looked into SWIG documentation section 5.3.5 which explains size_t as typedef but I could not get that working too.
Following is simpler code to reproduce the error:
C++ header:
#ifndef __EXAMPLE_H__
#define __EXAMPLE_H__
/* File: example.h */
#include <stdio.h>
#if defined(API_EXPORT)
#define APIEXPORT __declspec(dllexport)
#else
#define APIEXPORT __declspec(dllimport)
#endif
typedef int CNInt32;
class APIEXPORT ExampleClass {
public:
ExampleClass();
~ExampleClass();
void printFunction (int value);
void updateInt (CNInt32& var);
};
#endif //__EXAMPLE_H__
C++ Source:
/* File : example.cpp */
#include "example.h"
#include <iostream>
using namespace std;
/* I'm a file containing use of typedef variables */
ExampleClass::ExampleClass() {
}
ExampleClass::~ExampleClass() {
}
void ExampleClass::printFunction (int value) {
cout << "Value = "<< value << endl;
}
void ExampleClass::updateInt(CNInt32& var) {
var = 10;
}
Interface file:
/* File : example.i */
%module example
typedef int CNInt32;
%{
#include "example.h"
%}
%include <windows.i>
%include "example.h"
Python Code:
# file: runme.py
from example import *
# Try to set the values of some typedef variables
exampleObj = ExampleClass()
exampleObj.printFunction (20)
var = CNInt32(5)
exampleObj.updateInt (var)
Thanks again for your help.
Santosh
I got it working. I had to use typemaps in the interface file, see below:
- Thanks a lot to "David Froger" on Swig mailing lists.
- Also, thanks to doctorlove for initial hints.
%include typemaps.i
%apply CNInt32& INOUT { CNInt32& };
And then in python file:
var = 5 # Note: old code problematic line: var = CNInt32(5)
print "Python value = ",var
var = exampleObj.updateInt (var) # Note: 1. updated values returned automatically by wrapper function.
# 2. Multiple pass by reference also work.
# 3. It also works if your c++ function is returning some value.
print "Python Updated value var = ",var
Thanks again !
Santosh

Categories