swig shared_ptr results in an opaque object - python

I am trying to SWIG a C++ library to Python. One of the C++ functions returns a shared_ptr. I am successful at producing a Python module but the object returned by that function to Python appears to have no members. Is this a limitation of SWIG's handling of shared_ptr or am I doing something wrong?
This is roughly the structure of my code:
//foo.h
namespace MyNamespace
{
class Base {};
template <typename T> class Derived : public Base {};
std::shared_ptr<Base> make_obj();
}
SWIG:
//foo.i
%module foo
%include <std_shared_ptr.i>
%{
#define SWIG_FILE_WITH_INIT
#include "foo.h"
%}
%include "foo.h"
%template(FooA) MyNamespace::Derived< MyNamespace::AAA >;
%template(FooB) MyNamespace::Derived< MyNamespace::BBB >;
%shared_ptr(MyNamespace::Base)
%shared_ptr(FooA)
%shared_ptr(FooB)

I think you've got the order of things a little wrong here. Notice that in all the examples in the shared_ptr documentation they call %shared_ptr before any declaration/definition of that type is seen at all.
With the caveats commented about the %shared_ptr directive and the FooA/FooB template instances corrected for I believe something like this ought to work for your example.
//foo.i
%module foo
%include <std_shared_ptr.i>
%{
#define SWIG_FILE_WITH_INIT
#include "foo.h"
%}
%shared_ptr(MyNamespace::Base)
%shared_ptr(FooA) // This should probably be MyNamespace::Derived<...>
%shared_ptr(FooB) // This should probably be the fully qualified C++ type also
%include "foo.h"
%template(FooA) MyNamespace::Derived< MyNamespace::AAA >;
%template(FooB) MyNamespace::Derived< MyNamespace::BBB >;

Related

Accessing C nested structs in Python after interfacing with SWIG

Preface:
I have two header files: "Sample.h" and "Sample2.h". Here are the contents of the two headers:
"Sample.h" has:
#include "Sample2.h"
typedef struct {
int c;
sJustFloats sJf;
}sTest;
"Sample2.h" has:
typedef struct {
float a;
float b;
}sJustFloats;
Sample.c just has:
#include "Sample.h"
My SWIG interface file has:
%module Sample
%{
/* Put header files here or function declarations like below */
#include "Sample.h"
%}
%include "Sample.h"
%apply float {float a}; // For sJustFloats.a
%apply float {float b}; // For sJustFloats.b
%apply int {int c}; // For sTest.c
%apply sJustFloats {sJustFloats sJf}; // For sTest.sJf
%apply sTest {sTest test}; //I guess this exposes test struct itself.
Steps followed to get .so file that is imported in Python:
gcc -c -fPIC Sample.c Sample_wrap.c -I /usr/include/python3.6m
ld -shared Sample.o Sample_wrap.o -o _Sample.so
Observations:
I am able to import this as a module in Python and I can access fields of sTest as follows:
test = Sample.sTest()
test.c // To access "c" -- This works fine
Problem: I am not able to access fields of struct sJustFloats
If I do test.sJf(), it gives me TypeError: 'SwigPyObject' object is not callable. Similarly test.sJf.a gives: AttributeError: 'SwigPyObject' object has no attribute 'a'
Can someone please tell me how to access members of the struct, sJustFloats?
Additional observations:
When I do type(Sample.sTest), it gives me <class 'type'>, whereas doing type(Sample.sTest.sJf) gives me <class 'property'>.
If I have both structs in the same file "Sample.h", I am able to access all the fields by doing:
test = Sample.sTest()
sjf = test.sJf()
sjf.a # Works fine
sjf.b # Works fine
Am I missing something in the interface file? Are there are nuances of SWIG that I'm unaware of?
SWIG doesn’t recurse by default, but you need %include "Sample2.h" after %include "Sample.h" and don’t need any of the %apply:
%module Sample
%{
#include "Sample.h"
%}
%include "Sample.h"
%include "Sample2.h"

How to fix "swig/python detected a memory leak of type 'HWND *', no destructor found"?

Consider this mcve:
mcve.h
#pragma once
#include <windows.h>
class Windows {
public:
static HWND GetActiveWindow();
static HWND GetForegroundWindow();
};
mcve.cpp
#include "mcve.h"
HWND Windows::GetActiveWindow() {
return ::GetActiveWindow();
}
HWND Windows::GetForegroundWindow() {
return ::GetForegroundWindow();
}
mcve.i
%module mcve
%include <std_string.i>
%include <std_vector.i>
%include <typemaps.i>
%include <windows.i>
%include <cpointer.i>
%include <carrays.i>
%{
#include "mcve.h"
%}
%include "mcve.h"
setup.py
from distutils.core import Extension
from distutils.core import setup
setup(
name="mcve",
ext_modules=[
Extension(
"_mcve",
sources=["mcve.i", "mcve.cpp", "mcve_wrap.cpp"],
swig_opts=["-c++", "-python"],
include_dirs=["."],
library_dirs=[
"D:/software/vcpkg/installed/x86-windows/lib"
],
libraries=["user32"]
)
],
py_modules=["mcve"],
)
In order to run it, make sure you adjust properly the library_dirs variable and then just type:
python setup.py build_ext --inplace
If the extension has been generated succesfully then you just run python test.py and you should see an output similar to this:
>python test.py
swig/python detected a memory leak of type 'HWND *', no destructor found.
swig/python detected a memory leak of type 'HWND *', no destructor found.
I know you can use swig -nodefaultdtor to not generate implicit default destructors, I've tried and that didn't make any difference.
For the sake of completeness, here's roughly how the HWND typedefs would look like:
typedef PVOID HANDLE;
typedef HANDLE HWND;
typedef void *PVOID;
QUESTION: What's the proper way in swig to prevent that memory leak of HWND?
SWIG wasn't provided a definition of HWND and treats it as an opaque class. Note that %include "mcve.h" does NOT recurse into #include <windows.h> and only processes code directly in mcve.h, so SWIG has no definition at all.
There are a couple of solutions:
Give SWIG a definition that it understands. SWIG has a default for void*, and would correctly handle the following definition:
%module mcve
%{
#include "mcve.h"
%}
typedef void* HWND;
%include "mcve.h"
You can make custom typemaps instructing Python how to convert an HWND to and from a Python object. Unlike the previous solution, which returns a SWIG-wrapped void* object, this solution treats handles as integers:
%module mcve
%{
#include "mcve.h"
%}
%typemap(in) HWND %{
$1 = PyLong_AsVoidPtr($input);
%}
%typemap(out) HWND %{
$result = PyLong_FromVoidPtr($1);
%}
%include "mcve.h"

SWIG generated Python function annotations do not work

I recently started using swig to wrap one of my C++ libraries. I want to use this code in Python and it is working good so far. The Problem is that Python does not know what objects the wrapped functions return. Of course I can still call methods on the returned object because I know its type but I can't use intellisense in that way. This python code is meant to be used by other people and this problem makes coding just a little bit harder if one does not know the return types.
This is one of those methods:
def CreateJoinRequestMessage(self) -> "Hive::SC_Message":
return _Hive.SC_MessageHandler_CreateJoinRequestMessage(self)
It returns an SC_Message object. The SC_Message class is also defined in the python code produced by SWIG like this:
class SC_Message(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
def __init__(self, messageString: "std::string const &", messageType: "Hive::SC_MessageType const &"):
_Hive.SC_Message_swiginit(self, _Hive.new_SC_Message(messageString, messageType))
def GetContent(self) -> "std::string":
return _Hive.SC_Message_GetContent(self)
def GetMessageType(self) -> "Hive::SC_MessageType":
return _Hive.SC_Message_GetMessageType(self)
__swig_destroy__ = _Hive.delete_SC_Message
_Hive.SC_Message_swigregister(SC_Message)
So I should really be able to see the GetContent() method when using intellisense. When I change the function annotation in the generated Python code to "SC_Message" everything works as expected. Is this expected behavior by SWIG or can SWIG also produce more helpful annotations? Without the -py3 option there would be no annotations.
My SWIG command is the following:
swig -c++ -python -py3 hive.i
And this is my SWIG file:
%module Hive
%{
#define SWIG_FILE_WITH_INIT
#include "include/hive/PieceType.hpp"
#include "include/hive/MoveType.hpp"
#include "include/hive/AxialPosition.hpp"
#include "include/hive/Color.hpp"
#include "include/hive/neighbourMap.hpp"
#include "include/hive/Piece.hpp"
#include "include/hive/Player.hpp"
#include "include/hive/PieceStack.hpp"
#include "include/hive/globals.hpp"
#include "include/hive/Move.hpp"
#include "include/hive/board.hpp"
#include "include/hive/gameState.hpp"
#include "include/communication/SC_MessageType.hpp"
#include "include/communication/SC_Message.hpp"
#include "include/communication/SC_MessageHandler.hpp"
#include "include/hive/benchmark/benchmark.hpp"
%}
%include "std_string.i"
%include "std_array.i"
%include "std_pair.i"
%include "std_vector.i"
%include "include/hive/PieceType.hpp"
%include "include/hive/MoveType.hpp"
%include "include/hive/AxialPosition.hpp"
%include "include/hive/Color.hpp"
%include "include/hive/neighbourMap.hpp"
%include "include/hive/Piece.hpp"
%include "include/hive/Player.hpp"
%include "include/hive/PieceStack.hpp"
%include "include/hive/globals.hpp"
%include "include/hive/Move.hpp"
%include "include/hive/board.hpp"
%include "include/hive/gameState.hpp"
%include "include/communication/SC_MessageType.hpp"
%include "include/communication/SC_Message.hpp"
%include "include/communication/SC_MessageHandler.hpp"
%include "include/hive/benchmark/benchmark.hpp"
Thanks in advance.
It seems like SWIG usually generates annotations with c and c++ types. This will be addressed in version 2.0 as far as I can tell. For further information look here https://github.com/SimpleITK/SimpleITK/issues/809

SWIG_AsVal_wchar_t identifier not found

I'm having some issues with SWIG and wchat_t types, to reproduce the issue I got a little MCVE here
The problem is SWIG_AsVal_wchar_t is called but it's not defined anywhere.
I've tried following the accepted answer here but for some reason didn't work for me
How could I solve this?
PS: I've also posted the issue on github
In order to use wchar_t, you could include the interface cwstring.i instead of wchar.i.
This allows to build your sample with this modified libsystem.i :
%module libsystem
%include "cwstring.i"
%{
#include "foo.h"
%}
%include "foo.h"
An other way is to include the missing fragment using the following libsystem.i:
%module libsystem
%include "wchar.i"
%include <typemaps/wstring.swg>
%{
#include "foo.h"
%}
%include "foo.h"

Preprocessor macro in SWIG

I'm trying to get SWIG to recognize a simple preprocessor macro that "defines" a new function based on another definition and more complicated function. So, in the C header file I have:
#define FOO 1
#define my_macro_fun(args) my_fun(args,FOO)
SWIG sees and successfully wraps my_fun, but I want it to wrap my_macro_fun instead.
SWIG tries to spot macros that are constants and wrap them, but it won't be able to do anything smart with a macro like that. Fortunately there's an easy work around. Imagine you have the following header file:
#define FOO 1
#define my_macro_fun(args) my_fun(args,FOO)
void my_fun(int a, int b);
You can wrap it like:
%module test
%{
#include "test.h"
%}
%include "test.h"
which skips the my_macro_fun function. To get SWIG to wrap that though all you need to do is:
%module test
%{
#include "test.h"
%}
// Lie! Tell SWIG that it should be wrapped like any other function.
void my_macro_fun(int);
// This is entirely optional: it will cause my_fun to be wrapped as well
%include "test.h"
This little lie is perfectly fine in SWIG - it'll generate Wrapper code that assumes my_macro_fun(int) is callable, exactly like you would if you were using the macro. When compiling the wrapper the compiler will end up using the macro there and nobody is any the wiser.
Note that the order is important - the function that's really a macro needs to come before the %include in the interface file otherwise SWIG will try to expand the macro during the parsing of your declaration which makes for a syntax error. You can skip the %include entirely, or use a %ignore as well if you want to include it for other parts but suppress the original my_fun in the generated interface.
With some SWIG languages (e.g. Python) you can also use the typemap default:
%module test
%{
#include "test.h"
%}
%typemap(default) int b {
$1 = FOO;
}
%include "test.h"
To supply a value for an argument if none is given for it.

Categories