I'm trying to make a simple example to wrap an abstract C++ class with pybind. The code is:
#include <pybind11/pybind11.h>
#include <ostream>
#include <iostream>
namespace py = pybind11;
using namespace std;
class Base
{
public:
virtual void test() = 0;
};
class Derived: public Base
{
public:
void test() {cout << "Test";}
};
PYBIND11_MODULE(example,m) {
py::class_<Base, Derived>(m, "Base")
.def(py::init<>())
.def("test", &Derived::test);
}
And while I run the following command
c++ -O3 -Wall -shared -std=c++11 -fPIC `python3 -m pybind11 --includes` abstract_test.cpp -o example`python3-config --extension-suffix`\n
I get the error:
In file included from abstrakt_test.cpp:1:
/home/anaconda3/envs/pybind/lib/python3.8/site-packages/pybind11/include/pybind11/pybind11.h: In instantiation of ‘Return (Derived::* pybind11::method_adaptor(Return (Class::*)(Args ...)))(Args ...) [with Derived = Base; Return = void; Class = Derived; Args = {}]’:
/home/anaconda3/envs/pybind/lib/python3.8/site-packages/pybind11/include/pybind11/pybind11.h:1118:45: required from ‘pybind11::class_<type_, options>& pybind11::class_<type_, options>::def(const char*, Func&&, const Extra& ...) [with Func = void (Derived::*)(); Extra = {}; type_ = Base; options = {Derived}]’
abstrakt_test.cpp:23:36: required from here
/home/anaconda3/envs/pybind/lib/python3.8/site-packages/pybind11/include/pybind11/pybind11.h:1032:19: error: static assertion failed: Cannot bind an inaccessible base class method; use a lambda definition instead
static_assert(detail::is_accessible_base_of<Class, Derived>::value,
^~~~~~
/home/anaconda3/envs/pybind/lib/python3.8/site-packages/pybind11/include/pybind11/pybind11.h:1034:12: error: cannot convert ‘void (Derived::*)()’ to ‘void (Base::*)()’ in return
return pmf;
^~~
You need to "wrap" Base as well. Otherwise you are going to get following exception at import time:
ImportError: generic_type: type "Derived" referenced unknown base type "Base"
Also, wrapping order of Derived is wrong:
py::class_<Derived, Base>(m, "Derived")
Full example:
PYBIND11_MODULE(example,m) {
py::class_<Base>(m, "Base");
py::class_<Derived, Base>(m, "Derived")
.def(py::init<>())
.def("test", &Derived::test);
}
Related
This question builds on the question: How to instantiate a template method of a template class with swig? .
However, compared to that question, the code I'm trying to wrap is a little different:
class MyClass {
public:
template <class T>
void f1(const string& firstArg, const T& value);
};
The MyClass is a regular C++ class, with one template function f1.
Attempt to wrap MyClass::f1:, i.e. the Swig .i file
%template(f1String) MyClass::f1<std::string>;
With the above, a Python client can do
o = MyClass
str1 = "A String"
o.f1String("", str1)
This interface require the Python client to learn about all different f1 function names, each one different depending on the type. Not so clean.
A cleaner interface can be obtained by overloading, extending in the interface file, e.g.
%extend MyClass {
void f1(const string& s, const string& s1){
$self->f1(s, s1);
}
void f1(const string& s, const int& anInt){
$self->f1(s, anInt);
}
}
This allow client code like this:
o = MyClass
str1 = "A String"
anInt = 34
o.f1("", str1)
o.f1("", anInt)
Question is, is there any way to obtain the interface above (by extending), without extending, using Swig?
Luckily the Python wrapper supports overloading, so you can simply instantiate the two methods with the same name and SWIG will do its magic to resolve the overloads at runtime. See 6.18 Templates in the chapter “SWIG and C++” of the documentation for more details.
test.i
%module example
%{
#include<iostream>
class MyClass {
public:
template <class T>
void f1(const std::string& firstArg, const T& value) {
std::cout << firstArg << ',' << value << '\n';
}
};
%}
%include <std_string.i>
class MyClass {
public:
template <class T>
void f1(const std::string& firstArg, const T& value);
};
%extend MyClass {
%template(f1) f1<std::string>;
%template(f1) f1<int>;
}
test.py
from example import *
o = MyClass()
str1 = "A String"
anInt = 34
o.f1("X", str1)
o.f1("Y", anInt)
Example workflow to compile and run:
$ swig -python -c++ test.i
$ g++ -Wall -Wextra -Wpedantic -I /usr/include/python2.7/ -fPIC -shared test_wrap.cxx -o _example.so -lpython2.7
$ python2.7 test.py
X,A String
Y,34
I have this simple wrapper working and I am trying to make changes in order
to get the array all at once to use it in python.
test.cpp
#include <iostream>
using namespace std;
class Test{
public:
int ret(void);
};
int vec[10] = {0,1,2,3,4,5,6,7,8,9};
int Test::ret(){
return *vec; // This obviously won't work
}
extern "C" {
Test* Test_new(){ return new Test();}
int Test_ret(Test* test){ return test->ret();}
}
test.py
from ctypes import cdll
lib = cdll.LoadLibrary('./libtest.so')
class Test(object):
def __init__(self):
self.obj = lib.Test_new()
def ret(self):
return lib.Test_ret(self.obj)
dev = Test()
ret = dev.ret()
print ("Return", ret)
Compiling script
g++ -c -fPIC test.cpp -o test.o
g++ -shared -Wl,-soname,libtest.so -o libtest.so test.o
How can I do it?
I'm trying to make a generic stack in C++ and then trying to build a module of it and extend it to Python using SWIG.
For that, code in templated_stack.h is as follows
#include <string>
template <typename T>
class mNode {
public:
T data;
mNode* next;
/* mNode() { } */
mNode(T d) {
data = d;
next = NULL;
}
};
template <typename T>
class mStack {
mNode<T> *topOfStack;
mStack();
void push(T data);
T pop();
};
template <class T> mStack<T>::mStack() {
topOfStack = NULL;
}
template <class T> void mStack<T>::push(T data) {
mNode<T>* newNode = new mNode<T>(data);
newNode->next = topOfStack;
topOfStack = newNode;
}
template <class T> T mStack<T>::pop(void) {
mNode<T>* tempTop = topOfStack;
T dataToBePopped = tempTop->data;
topOfStack = topOfStack->next;
delete tempTop;
return dataToBePopped;
}
The interface file I've written is templated_stack.i as follows
%module TemplatedStack
%{
#include <string>
#include "templated_stack.h"
%}
%include "templated_stack.h"
%template(IntStack) mStack <int>;
And I'm compiling and building module by following script compileScript.sh which has following code
swig -c++ -python -o templated_stack_wrap.cpp templated_stack.i
g++ -c -fPIC templated_stack_wrap.cpp -I/usr/include/python2.7
g++ -shared -o _TemplatedStack.so templated_stack_wrap.o
The module is build successfully and also it is getting imported without any error, But when I try to make an object of IntStack as follows
from TemplatedStack import IntStack
t = IntStack()
I am getting following error
in __init__(self, *args, **kwargs)
75 __swig_getmethods__ = {}
76 __getattr__ = lambda self, name: _swig_getattr(self, IntStack, name)
---> 77 def __init__(self, *args, **kwargs): raise AttributeError("No constructor defined")
78 __repr__ = _swig_repr
79 __swig_destroy__ = _TemplatedStack.delete_IntStack
AttributeError: No constructor defined
Any help would be appreciated regarding the above error
Thanks in advance
The github link of repository is this
This is happening because all your members of the mStack class are private. SWIG can't wrap private things.
Either change the keyword class to struct, or add public: to the definition appropriately.
After reading an excellent answer on Dynamically rethrowing self-defined C++ exceptions as Python exceptions using SWIG, I am trying to throw a custom Exception from C++ and catch it in Python. I have tried to build the simplest example, so that I can see what I'm doing wrong. I would appreciate some help here.
I've started by creating a class called Example which has one method. In this method, I always throw my Exception which extends the Exception base class in C++. This exception is declared in the header.
I'm trying to catch this Exception in Python but so far I am unable to get this to compile. I tried some of the other solutions in the thread mentioned aboved, and while they compiled, I did not seem to be able to catch the Exception without Python aborting.
Here's what I have so far (a very basic project)
example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <string>
#include <exception>
class Example
{
public:
std::string getName();
};
class ExampleException
{
public:
ExampleException(const std::string &what) { this->eMsg == (std::string(what)); }
const char * what() const throw() { return eMsg.c_str(); }
private:
std::string eMsg;
};
#endif // EXAMPLE_H
example.cpp
#include "example.h"
#include "exception"
using namespace std;
std::string Example::getName()
{
/* Always throw an exception just so we can test this */
throw ExampleException("Couldn't find a name");
return("John");
}
example.i
%module example
%include "example.i"
%include exception.i
%init %{
m_ExampleException = PyErr_NewException("_example.ExampleException", NULL, NULL);
Py_INCREF(m_ExampleException);
PyModule_AddObject(m, "ExampleException", m_ExampleException);
%}
%exception {
try {
$action
} catch (ExampleException &e) {
PyErr_SetString(ExampleException, const_cast<char*>(e.what()));
SWIG_fail;
}
}
%{
#include "example.h"
#define SWIG_FILE_WITH_INIT
static PyObject* m_ExampleException;
%}
test.py
import example
try:
print example.Example_getName()
except ExampleException as e:
print e.what()
And I run the following commands to build the project:
swig -verbose -python -c++ example.i
g++ -fPIC -c example_wrap.cxx -I/usr/include/python2.7 -o example_wrap.o -fPIC
g++ -c example.cpp -I/usr/include/python2.7 -fPIC
g++ -shared example_wrap.o example.o -o _example.so
Unfortunately, I am given the following error message as well as a warning:
example_wrap.cxx: In function ‘void init_example()’:
example_wrap.cxx:3839:82: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
m_ExampleException = PyErr_NewException("_example.ExampleException", NULL, NULL);
^
In file included from /usr/include/python2.7/Python.h:80:0,
from example_wrap.cxx:171:
/usr/include/python2.7/object.h:769:20: error: expected primary-expression before ‘)’ token
((PyObject*)(op))->ob_refcnt++)
I feel like I'm missing something obvious and would appreciate an extra pair of eyes. I have not used SWIG and C++ together extensively before.
Thanks for your time
A.hpp
// A.hpp
#ifndef RECTANGLE_HPP
#define RECTANGLE_HPP
#include <iostream>
#include <vector>
namespace test {
double euclid_dist(const std::vector<double> & a) {
return 3.1415926;
}
template <class T,
double (*dist)(const std::vector<T> &)>
class foo {
public:
foo(std::vector<T> _items) {
items = _items;
}
~foo() {}
void inside() {
std::cout << "hello" << std::endl;
}
void func() {
inside();
}
void prt(std::vector<std::vector<T> > a) {
if(a.size() >= 1) {
std::cout << euclid_dist(a[0]) << std::endl;
}
}
private:
std::vector<T> items;
}; // class foo
} // namespace test
#endif
demo.pyx:
# demo.pyx
# distutils: language = c++
from libcpp.vector cimport vector
cdef extern from "A.hpp" namespace "test":
double euculid_dist(const vector [double] & a)
#def test_euclid_dist(const vector [double] a):
# return euclid_dist(a)
cdef extern from "A.hpp" namespace "test":
cdef cppclass foo [double, euclid_dist]:
foo (vector[double]) except +
void func()
void prt(vector[vector[double]])
cdef class PyFoo:
cdef foo *thisptr # hold a C++ instance which we're wrapping
def __cinit__(self, vec):
self.thisptr = new foo(vec)
def __dealloc__(self):
del self.thisptr
def func(self):
self.thisptr.func()
def prt(self, d):
self.thisptr.prt(d)
setup.py
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(ext_modules = cythonize(Extension(
"demo", # the extesion name
sources=["demo.pyx"], # the Cython source and additional C++ source files
language="c++", # generate and compile C++ code
)))
When compiling with python setup.py build_ext --inplace, I get error below:
Compiling demo.pyx because it changed.
Cythonizing demo.pyx
running build_ext
building 'demo' extension
x86_64-pc-linux-gnu-g++ -pthread -fPIC -I/usr/include/python2.7 -c
demo.cpp -o build/temp.linux-x86_64-2.7/rect.o
demo.cpp:551:20: error: ‘euclid_dist’ was not declared in this scope
demo.cpp:551:31: error: template argument 2 is invalid
demo.cpp: In function ‘int __pyx_pf_4rect_5PyFoo___cinit__(__pyx_obj_4rect_PyFoo*, PyObject*)’:
demo.cpp:866:20: error: ‘euclid_dist’ was not declared in this scope
demo.cpp:866:31: error: template argument 2 is invalid
demo.cpp:866:43: error: invalid type in declaration before ‘;’ token
demo.cpp:881:38: error: ‘euclid_dist’ cannot appear in a constant-expression
demo.cpp:881:49: error: template argument 2 is invalid
demo.cpp:881:60: error: cannot convert ‘std::vector<double>’ to ‘int’ in initialization
demo.cpp: In function ‘PyObject* __pyx_pf_4rect_5PyFoo_4func(__pyx_obj_4rect_PyFoo*)’:
demo.cpp:984:26: error: request for member ‘func’ in ‘* __pyx_v_self->__pyx_obj_4rect_PyFoo::thisptr’, which is of non-class type ‘int’
demo.cpp: In function ‘PyObject* __pyx_pf_4rect_5PyFoo_6prt(__pyx_obj_4rect_PyFoo*, PyObject*)’:
demo.cpp:1038:26: error: request for member ‘prt’ in ‘* __pyx_v_self->__pyx_obj_4rect_PyFoo::thisptr’, which is of non-class type ‘int’
error: command 'x86_64-pc-linux-gnu-g++' failed with exit status 1
It seems that in scope it can not find euclid_dist function, but I think it is in global scope..What's wrong with above code? Thanks!
Cython doesn't yet have support for non type template parameters, there is a bit of a hacky workaround for this though.
When you create the cython definition of the class you give it the explicit template instantiation so that cython won't need to know about the templated nature of the class.
In your case the definition for foo would be:
cdef extern from "A.hpp" namespace "test":
cdef cppclass foo "test::foo<double, test::euclid_dist>":
foo(const vector[double] &) except +
#... other members