I'm using SWIG 2.0 to create a Python wrapper for a C++ library. One method has an argument of type "const std::map&". SWIG happily generates a wrapper for it, but I can't figure out how to invoke the method. If I pass, for example, {"a":"b"} for that argument, I get a "NotImplementedError: Wrong number or type of arguments for overloaded function" error.
I looked at the generated .cxx file in the hope it would clarify, but it didn't. Here's the code that processes that argument:
res4 = SWIG_ConvertPtr(obj3, &argp4, SWIGTYPE_p_std__mapT_std__string_std__string_t, 0 | 0);
if (!SWIG_IsOK(res4)) {
SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "new_Context" "', argument " "4"" of type '" "std::map< std::string,std::string > const &""'");
}
It clearly knows that argument exists, and that it's supposed to be something that gets converted to a map. But I can't figure out what it actually wants me to pass for it.
When you're using a C++ template (e.g. a std::map<string, string>) you need to create an alias for it in your .i file so you can use it in python:
namespace std {
%template(map_string_string) map<string, string>;
}
Now let's say you want to wrap a function that looks like this:
void foo(const std::map<string, string> &arg);
On the python side, you need to pass a map_string_string to foo, not a python dict. It turns out that you can easily convert a python dict to a map though by doing this:
map_string_string({ 'a' : 'b' })
so if you want to call foo, you need to do this:
foo(map_string_string({ 'a' : 'b' }))
Here's full example code that works.
// test.i
%module test
%include "std_string.i"
%include "std_map.i"
namespace std {
%template(map_string_string) map<string, string>;
}
void foo(const std::map<std::string, std::string> &val);
%{
#include <iostream>
#include <string>
#include <map>
using namespace std;
void
foo(const map<string, string> &val)
{
map<string, string>::const_iterator i = val.begin();
map<string, string>::const_iterator end = val.end();
while (i != end) {
cout << i->first << " : " << i->second << endl;
++i;
}
}
%}
And the python test code:
#run_test.py
import test
x = test.map_string_string({ 'a' : 'b', 'c' : 'd' })
test.foo(x)
And my command line:
% swig -python -c++ test.i
% g++ -fPIC -shared -I/usr/include/python2.7 -o _test.so test_wrap.cxx
% python run_test.py
a : b
c : d
Related
I'm messing around with (a bit old) SimIt-ARM 3.0. The SimIt-ARM 3.0 has issgen (ISS generator) based on (ARMv5) ISA defined using internal language defined using Lex specification & Yacc grammar. In particular it has a symbol_table class based on STL hash_map (which is not part of the C++ Standard Library, and which is now deprecated).
The problem: the hash_map behaves unexpectedly. Here is a demo using the original test. We see that output differs between -O0 and -O1.
Can someone help me to figure out what it the root cause?
UPD: here is the MRE:
#ifdef __GNUC__
#if __GNUC__ < 3
#include <hash_map.h>
namespace Sgi { using ::hash_map; using ::hash; }; // inherit globals
#else
#include <ext/hash_map>
#if __GNUC_MINOR__ == 0 && __GNU_C__ == 3
namespace Sgi = std; // GCC 3.0
#else
namespace Sgi = ::__gnu_cxx; // GCC 3.1 and later
#endif
#endif
#endif
#include <string>
#include <iostream>
#include <vector>
#include <cstring>
struct strEql
{
bool operator()(const char* sz1, const char* sz2)
{
return strcmp(sz1,sz2) == 0;
}
};
typedef Sgi::hash_map<const char *, unsigned int,
Sgi::hash<char *>, strEql> hash_map;
hash_map hasher;
unsigned int idx = 1;
void insert(const std::string& key)
{
hash_map::iterator it = hasher.find(key.c_str());
if (it==hasher.end())
{
hasher[key.c_str()] = idx++;
}
}
void print_hasher(void)
{
for(hash_map::iterator it = hasher.begin(); it != hasher.end(); it++)
{
std::cout << "fisrt: " << it->first << ", second: " << it->second << std::endl;
}
}
int main(void)
{
insert("xxx");
insert("yyy");
print_hasher();
}
Invocations and output:
$ g++ mre.cpp -Wno-deprecated -O0
fisrt: xxx, second: 1
fisrt: yyy, second: 2
$ g++ mre.cpp -Wno-deprecated -O1
fisrt: xxx, second: 1
In insert you are creating a temporary std::string key, the pointer returned from key.c_str() is therefore only valid until the end of the function.
Even if it was still valid then multiple calls to insert with the same string wouldn't necessarily produce the same address, equally calls with different strings might produce the same address so your hash table will be completely broken.
If you really want to hash based on the addresses you need to make sure you are only using string literals which will always have the same address (though I don't think there is any guarantee that two literals with the same value will have the same address) and will be valid for the duration of your program:
void insert(const char* key)
{
hash_map::iterator it = hasher.find(key);
if (it==hasher.end())
{
hasher[key] = idx++;
}
}
https://godbolt.org/z/6r18T16P9
You still need to be careful as its easily possible to pass a non-literal to insert and you'll be back to your original problem:
insert(std::string("yyy").c_str());
https://godbolt.org/z/EaGMvhPrE
I have written a C++ library to extract simulation data (= simple vectors with (x,y) or (x,y,z) components) from electronic design automation (EDA) tools. More concrete, this data represents electrical signals for different points in time.
The C++ library offers several methods. Two important ones are:
std::vector<std::string> getSignalNames() // Returns all signal names in the file
std::vector<std::vector<double>> getSignals() // Returns the actual data as M x N matrix (with M rows and N columns)
Using the library in C++ works perfectly and yields the expected results, e.g.:
getSignalNames():
Signal1
Signal2
getSignals():
1 1 1 2
2 1 2 3
Perl programmers asked me to also offer the library to them and I decided to use the wrapper generator SWIG to create bindings. I worked through the tutorial and I was able to successfully set up a minimal working example.
Based on the example, I wrote a complete SWIG interface file for the C++ library. The wrapper generation and build process works smoothly and I can also use getSignalNames() without any problems:
// Perl snippet to read out signal names
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalNames = $parserPointer->getSignalNames();
foreach my $signalName ( #$signalNames ) {
print "$signalName\n";
}
// Output:
Signal1
Signal2
But, I ran into trouble when using the return value from getSignals():
// Perl snippet to read out the actual signal data
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalData = $parserPointer->getSignals();
foreach my $rowAsHashRef ( #$signalData ) {
print "reftype: " . reftype($rowAsHashRef) . "\n";
print "keys: " . keys(%$rowAsHashRef) . "\n"
}
// Output:
reftype: HASH
keys: 0
reftype: HASH
keys: 0
As you see, each row is represented as hash in Perl, but there are no keys in the Hash. Nevertheless, when using Perl's Data::Dumper, I can see the correct data type for each row:
my $parserPointer = new waveformparser::ScopeParser("input.file");
$signalData = $parserPointer->getSignals();
print Dumper $signalData;
// Output:
$VAR1 = [
bless( {}, 'waveformparser::vector_1d_double' ),
bless( {}, 'waveformparser::vector_1d_double' )
];
I.e., according to the data dumper, each row consists of several columns (i.e., 'waveformparser::vector_1d_double') which are defined in the SWIG interface file as following:
...
%include "std_vector.i"
%template(vector_1d_double) std::vector<double>;
%template(vector_2d_double) std::vector<std::vector<double>>;
...
My question is now: How can I access elements of this "blessed" (wrapped) vector_1d_double objects in Perl?
I thought, SWIG would provide convenient access methods for such objects. I.e., the underlying C++ data type is just a simple 1d vector of doubles (std::vector<double>).
You need to write an output typemap for std::vector<std::vector<double>> to convert to a proper Perl array of arrays. Here is an example:
VecVec.i:
%module VecVec
%typemap(out) std::vector<std::vector<double>> {
AV *array = (AV *) newAV();
auto vec_ptr = &$1;
std::vector<std::vector<double>>& vec = *vec_ptr;
for (const auto &item : vec) {
AV *subarray = (AV *) newAV();
for (const auto &subitem : item) {
av_push(subarray, newSVnv((NV) subitem));
}
av_push(array, (SV*) newRV_noinc((SV*)subarray));
}
$result = newRV_noinc((SV*) array);
sv_2mortal($result);
argvi++;
}
%{
#include "VecVec.h"
%}
%include "VecVec.h"
VecVec.h:
#ifndef VEVEC_H_
#define VECVEC_H_
#include <vector>
class VecVec
{
public:
VecVec() {}
~VecVec() {}
std::vector<std::vector<double>> getSignals();
private:
};
#endif
VecVec.cpp:
#include <string>
#include <vector>
#include <iostream>
#include "VecVec.h"
std::vector<std::vector<double>> VecVec::getSignals()
{
std::vector<std::vector<double>> vec {
{1, 2, 3},
{4, 5, 6}
};
return vec;
}
Then compile with:
perl_include_dir=$(perl -MConfig -e'print $Config{archlib}')"/CORE"
swig -perl5 -c++ -I/usr/include VecVec.i
g++ -fPIC -c VecVec.cpp
g++ -I${perl_include_dir} -c -fPIC -g -o VecVec_wrap.o VecVec_wrap.cxx
g++ -shared -L. VecVec.o VecVec_wrap.o -o VecVec.so
and test the module with test.pl:
use strict;
use warnings;
use Data::Dumper qw(Dumper);
use lib '.';
use VecVec;
my $p = VecVec::VecVec->new();
my $sig = $p->getSignals();
print Dumper($sig);
Output:
$VAR1 = [
[
'1',
'2',
'3'
],
[
'4',
'5',
'6'
]
];
Re:
I thought, SWIG would provide convenient access methods for such objects. I.e., the underlying C++ data type is just a simple 1d vector of doubles (std::vector).
It should. You do need to instantiate the templates before SWIG processes the functions, but you'll need to show a minimal, reproducible example to know for sure why it isn't working.
I'm not currently familiar with building a Perl extension, but here's a non-language-specific SWIG .i file that should work for Perl. I'll demonstrate with Python:
test.i
%module test
// Define templates before SWIG processes the code.
%include "std_vector.i"
%include "std_string.i"
%template(vector_string) std::vector<std::string>;
%template(vector_1d_double) std::vector<double>;
%template(vector_2d_double) std::vector<std::vector<double>>;
%inline %{
#include <vector>
#include <string>
std::vector<std::string> getSignalNames() {
return {"abc","123"};
}
std::vector<std::vector<double>> getSignals() {
return {{1.1,2.2},{3.3,4.4}};
}
%}
The above includes everything in a standalone file. I used swig -c++ -python test.i and compiled the resulting test_wrap.cxx file as a Python extension.
Demo:
>>> import test
>>> test.getSignalNames()
('abc', '123')
>>> test.getSignals()
((1.1, 2.2), (3.3, 4.4))
If SWIG's Perl support is comparable this .i file should work for you without defining your own output typemaps.
I'm trying to wrap a C++ function that can receive Lua table of strings and use it as an array of strings in C++ function.
I could successfully do this using float type instead of string.
Here's my function.
static void readTable(float values[], int len) {
for (int i=0; i<len; ++i)
printf("VALUE : %g", values[i]);
}
And here's the typemaps part from SWIG interface (.i) file
// using typemaps
%include <typemaps.i>
%apply (float INPUT[], int) {(float values[], int len)};
It works fine when I call this function in Lua.
However, if I change the type to std::string instead of float and pass table of strings to the function, I get the following error in Lua.
Error in readTable expected 2..2 args, got 1
I don't know what this means and how to fix this.
Maybe I have to add something more to the SWIG interface (.i) file?
I would appreciate any help. Thanks!
The typemaps.i file only defines typemaps for arrays of the primitive numeric types.
Thus I recommend that you write your own typemap. Then you can also take an argument of type std::vector<std::string>, so you don't even need the length parameter.
%module table_of_strings
%{
#include <iostream>
#include <string>
#include <vector>
void readTable(std::vector<std::string> values) {
for (size_t i=0; i<values.size(); ++i) {
std::cout << "VALUE : " << values[i] << '\n';
}
}
%}
%include "exception.i"
%typemap(in) std::vector<std::string>
{
if (!lua_istable(L,1)) {
SWIG_exception(SWIG_RuntimeError, "argument mismatch: table expected");
}
lua_len(L,1);
size_t len = lua_tointeger(L,-1);
$1.reserve(len);
for (size_t i = 0; i < len; ++i) {
lua_pushinteger(L,i+1);
lua_gettable(L,1);
$1.push_back(lua_tostring(L,-1));
}
}
void readTable(std::vector<std::string> values);
swig -c++ -lua test.i
clang++ -Wall -Wextra -Wpedantic -I/usr/include/lua5.3 -fPIC -shared test_wrap.cxx -o table_of_strings.so -llua5.3
local tos = require"table_of_strings"
tos.readTable({"ABC", "DEF", "GHI"})
VALUE : ABC
VALUE : DEF
VALUE : GHI
I've written part of a class in C++ and I want to be able to use it in conjunction with a Python GUI, so I'm using Boost.Python to try and make it easy. The issue I'm running into is that in following their guide (http://www.boost.org/doc/libs/1_55_0/libs/python/doc/tutorial/doc/html/python/exposing.html), I keep getting the following exception whenever I run bjam:
PacketWarrior/pcap_ext.cc:21:5: error: too few template arguments for class template 'class_'
Obviously it's complaining at me for omitting what they claim are optional arguments to the 'class_' template function, but I can't figure out why. I'm assuming it's a compiler issue but I don't know how to fix it. I'm running OS X 10.9 and using darwin for the default toolset, but GCC throws the same error. My Boost version is 1_55_0 if that helps at all.
Class header file (header guards omitted):
#include <queue>
#include "pcap.h"
#include "Packet.h"
class PacketEngine {
public:
PacketEngine();
~PacketEngine();
const char** getAvailableDevices(char *error_buf);
bool selectDevice(const char* dev);
Packet getNextPacket();
private:
char *selected_device;
char **devices;
int num_devices;
std::queue<Packet> packet_queue;
};
The cc file containing the references to Boost.Python and my class:
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include "PacketEngine.h"
BOOST_PYTHON_MODULE(pcap_ext) {
using namespace boost::python;
class_<PacketEngine>("PacketEngine")
.def("getAvailableDevices", &PacketEngine::getAvailableDevices);
}
And my bjam file (irrelevant parts and comments omitted):
use-project boost : ../../../Downloads/boost_1_55_0 ;
project
: requirements <library>/boost/python//boost_python
<implicit-dependency>/boost//headers
: usage-requirements <implicit-dependency>/boost//headers
;
python-extension pcap_ext : PacketWarrior/pcap_ext.cc ;
install convenient_copy
: pcap_ext
: <install-dependencies>on <install-type>SHARED_LIB <install-type>PYTHON_EXTENSION
<location>.
;
local rule run-test ( test-name : sources + )
{
import testing ;
testing.make-test run-pyd : $(sources) : : $(test-name) ;
}
run-test pcap : pcap_ext pcap.py ;
Any ideas as to how to circumvent this exception are greatly appreciated! I looked into the obvious route of just adding the optional parameters but I don't think they're relevant to my project. The class_ definition can be found here:
http://www.boost.org/doc/libs/1_37_0/libs/python/doc/v2/class.html
In short, include either:
boost/python.hpp: The Boost.Python convenient header file.
boost/python/class.hpp: The header that defines boost::python::class_.
The current included header files are declaring class_ with no default template arguments from def_visitor.hpp.
Also, trying to directly expose PacketEngine::getAvailableDevices() will likely present a problem:
It accepts a char* argument, but strings are immutable in Python.
There are no types that automatically convert to/from a const char** in Boost.Python.
It may be reasonable for a Python user to expect PacketEngine.getAvailableDevices() to return an iterable type containing Python strs, or throw an exception on error. This can be accomplished in a non-intrusive manner by writing a helper or auxiliary function that delegates to original function, but is exposed to Python as PacketEngine.getAvailableDevices().
Here is a complete example based on the original code:
#include <exception> // std::runtime_error
#include <boost/python.hpp>
namespace {
const char* devices_str[] = {
"device A",
"device B",
"device C",
NULL
};
} // namespace
class PacketEngine
{
public:
PacketEngine() : devices(devices_str) {}
const char** getAvailableDevices(char *error_buf)
{
// Mockup example to force an error on second call.
static bool do_error = false;
if (do_error)
{
strcpy(error_buf, "engine not responding");
}
do_error = true;
return devices;
}
private:
const char **devices;
};
/// #brief Auxiliary function for PacketEngine::getAvailableDevices that
/// provides a more Pythonic API. The original function accepts a
/// char* and returns a const char**. Both of these types are
/// difficult to use within Boost.Python, as strings are immutable
/// in Python, and Boost.Python is focused to providing
/// interoperability to C++, so the const char** type has no direct
/// support.
boost::python::list PacketEngine_getAvailableDevices(PacketEngine& self)
{
// Get device list and error from PacketEngine.
char error_buffer[256] = { 0 };
const char** devices = self.getAvailableDevices(error_buffer);
// On error, throw an exception. Boost.Python will catch it and
// convert it to a Python's exceptions.RuntimeError.
if (error_buffer[0])
{
throw std::runtime_error(error_buffer);
}
// Convert the c-string array to a list of Python strings.
namespace python = boost::python;
python::list device_list;
for (unsigned int i = 0; devices[i]; ++i)
{
const char* device = devices[i];
device_list.append(python::str(device, strlen(device)));
}
return device_list;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<PacketEngine>("PacketEngine")
.def("getAvailableDevices", &PacketEngine_getAvailableDevices);
}
Interactive usage:
>>> import example
>>> engine = example.PacketEngine()
>>> for device in engine.getAvailableDevices():
... print device
...
device A
device B
device C
>>> devices = engine.getAvailableDevices()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: engine not responding
I have a class interface written in C++. I have a few classes that implement this interface also written in C++. These are called in the context of a larger C++ program, which essentially implements "main". I want to be able to write implementations of this interface in Python, and allow them to be used in the context of the larger C++ program, as if they had been just written in C++.
There's been a lot written about interfacing python and C++ but I cannot quite figure out how to do what I want. The closest I can find is here: http://www.cs.brown.edu/~jwicks/boost/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions, but this isn't quite right.
To be more concrete, suppose I have an existing C++ interface defined something like:
// myif.h
class myif {
public:
virtual float myfunc(float a);
};
What I want to be able to do is something like:
// mycl.py
... some magic python stuff ...
class MyCl(myif):
def myfunc(a):
return a*2
Then, back in my C++ code, I want to be able to say something like:
// mymain.cc
void main(...) {
... some magic c++ stuff ...
myif c = MyCl(); // get the python class
cout << c.myfunc(5) << endl; // should print 10
}
I hope this is sufficiently clear ;)
There's two parts to this answer. First you need to expose your interface in Python in a way which allows Python implementations to override parts of it at will. Then you need to show your C++ program (in main how to call Python.
Exposing the existing interface to Python:
The first part is pretty easy to do with SWIG. I modified your example scenario slightly to fix a few issues and added an extra function for testing:
// myif.h
class myif {
public:
virtual float myfunc(float a) = 0;
};
inline void runCode(myif *inst) {
std::cout << inst->myfunc(5) << std::endl;
}
For now I'll look at the problem without embedding Python in your application, i.e. you start excetion in Python, not in int main() in C++. It's fairly straightforward to add that later though.
First up is getting cross-language polymorphism working:
%module(directors="1") module
// We need to include myif.h in the SWIG generated C++ file
%{
#include <iostream>
#include "myif.h"
%}
// Enable cross-language polymorphism in the SWIG wrapper.
// It's pretty slow so not enable by default
%feature("director") myif;
// Tell swig to wrap everything in myif.h
%include "myif.h"
To do that we've enabled SWIG's director feature globally and specifically for our interface. The rest of it is pretty standard SWIG though.
I wrote a test Python implementation:
import module
class MyCl(module.myif):
def __init__(self):
module.myif.__init__(self)
def myfunc(self,a):
return a*2.0
cl = MyCl()
print cl.myfunc(100.0)
module.runCode(cl)
With that I was then able to compile and run this:
swig -python -c++ -Wall myif.i
g++ -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7
python mycl.py
200.0
10
Exactly what you'd hope to see from that test.
Embedding the Python in the application:
Next up we need to implement a real version of your mymain.cc. I've put together a sketch of what it might look like:
#include <iostream>
#include "myif.h"
#include <Python.h>
int main()
{
Py_Initialize();
const double input = 5.0;
PyObject *main = PyImport_AddModule("__main__");
PyObject *dict = PyModule_GetDict(main);
PySys_SetPath(".");
PyObject *module = PyImport_Import(PyString_FromString("mycl"));
PyModule_AddObject(main, "mycl", module);
PyObject *instance = PyRun_String("mycl.MyCl()", Py_eval_input, dict, dict);
PyObject *result = PyObject_CallMethod(instance, "myfunc", (char *)"(O)" ,PyFloat_FromDouble(input));
PyObject *error = PyErr_Occurred();
if (error) {
std::cerr << "Error occured in PyRun_String" << std::endl;
PyErr_Print();
}
double ret = PyFloat_AsDouble(result);
std::cout << ret << std::endl;
Py_Finalize();
return 0;
}
It's basically just standard embedding Python in another application. It works and gives exactly what you'd hope to see also:
g++ -Wall -Wextra -I/usr/include/python2.7 main.cc -o main -lpython2.7
./main
200.0
10
10
The final piece of the puzzle is being able to convert the PyObject* that you get from creating the instance in Python into a myif *. SWIG again makes this reasonably straightforward.
First we need to ask SWIG to expose its runtime in a headerfile for us. We do this with an extra call to SWIG:
swig -Wall -c++ -python -external-runtime runtime.h
Next we need to re-compile our SWIG module, explicitly giving the table of types SWIG knows about a name so we can look it up from within our main.cc. We recompile the .so using:
g++ -DSWIG_TYPE_TABLE=myif -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7
Then we add a helper function for converting the PyObject* to myif* in our main.cc:
#include "runtime.h"
// runtime.h was generated by SWIG for us with the second call we made
myif *python2interface(PyObject *obj) {
void *argp1 = 0;
swig_type_info * pTypeInfo = SWIG_TypeQuery("myif *");
const int res = SWIG_ConvertPtr(obj, &argp1,pTypeInfo, 0);
if (!SWIG_IsOK(res)) {
abort();
}
return reinterpret_cast<myif*>(argp1);
}
Now this is in place we can use it from within main():
int main()
{
Py_Initialize();
const double input = 5.5;
PySys_SetPath(".");
PyObject *module = PyImport_ImportModule("mycl");
PyObject *cls = PyObject_GetAttrString(module, "MyCl");
PyObject *instance = PyObject_CallFunctionObjArgs(cls, NULL);
myif *inst = python2interface(instance);
std::cout << inst->myfunc(input) << std::endl;
Py_XDECREF(instance);
Py_XDECREF(cls);
Py_Finalize();
return 0;
}
Finally we have to compile main.cc with -DSWIG_TYPE_TABLE=myif and this gives:
./main
11
Minimal example; note that it is complicated by the fact that Base is not pure virtual. There we go:
baz.cpp:
#include<string>
#include<boost/python.hpp>
using std::string;
namespace py=boost::python;
struct Base{
virtual string foo() const { return "Base.foo"; }
// fooBase is non-virtual, calling it from anywhere (c++ or python)
// will go through c++ dispatch
string fooBase() const { return foo(); }
};
struct BaseWrapper: Base, py::wrapper<Base>{
string foo() const{
// if Base were abstract (non-instantiable in python), then
// there would be only this->get_override("foo")() here
//
// if called on a class which overrides foo in python
if(this->get_override("foo")) return this->get_override("foo")();
// no override in python; happens if Base(Wrapper) is instantiated directly
else return Base::foo();
}
};
BOOST_PYTHON_MODULE(baz){
py::class_<BaseWrapper,boost::noncopyable>("Base")
.def("foo",&Base::foo)
.def("fooBase",&Base::fooBase)
;
}
bar.py
import sys
sys.path.append('.')
import baz
class PyDerived(baz.Base):
def foo(self): return 'PyDerived.foo'
base=baz.Base()
der=PyDerived()
print base.foo(), base.fooBase()
print der.foo(), der.fooBase()
Makefile
default:
g++ -shared -fPIC -o baz.so baz.cpp -lboost_python `pkg-config python --cflags`
And the result is:
Base.foo Base.foo
PyDerived.foo PyDerived.foo
where you can see how fooBase() (the non-virtual c++ function) calls virtual foo(), which resolves to the override regardless whether in c++ or python. You could derive a class from Base in c++ and it would work just the same.
EDIT (extracting c++ object):
PyObject* obj; // given
py::object pyObj(obj); // wrap as boost::python object (cheap)
py::extract<Base> ex(pyObj);
if(ex.check()){ // types are compatible
Base& b=ex(); // get the wrapped object
// ...
} else {
// error
}
// shorter, thrwos when conversion not possible
Base &b=py::extract<Base>(py::object(obj))();
Construct py::object from PyObject* and use py::extract to query whether the python object matches what you are trying to extract: PyObject* obj; py::extract<Base> extractor(py::object(obj)); if(!extractor.check()) /* error */; Base& b=extractor();
Quoting http://wiki.python.org/moin/boost.python/Inheritance
"Boost.Python also allows us to represent C++ inheritance relationships so that wrapped derived classes may be passed where values, pointers, or references to a base class are expected as arguments."
There are examples of virtual functions so that solves the first part (the one with class MyCl(myif))
For specific examples doing this, http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions
For the line myif c = MyCl(); you need to expose your python (module) to C++. There are examples here http://wiki.python.org/moin/boost.python/EmbeddingPython
Based upon the (very helpful) answer by Eudoxos I've taken his code and extended it such that there is now an embedded interpreter, with a built-in module.
This answer is the Boost.Python equivalent of my SWIG based answer.
The headerfile myif.h:
class myif {
public:
virtual float myfunc(float a) const { return 0; }
virtual ~myif() {}
};
Is basically as in the question, but with a default implementation of myfunc and a virtual destructor.
For the Python implementation, MyCl.py I have basically the same as the question:
import myif
class MyCl(myif.myif):
def myfunc(self,a):
return a*2.0
This then leaves mymain.cc, most of which is based upon the answer from Eudoxos:
#include <boost/python.hpp>
#include <iostream>
#include "myif.h"
using namespace boost::python;
// This is basically Eudoxos's answer:
struct MyIfWrapper: myif, wrapper<myif>{
float myfunc(float a) const {
if(this->get_override("myfunc"))
return this->get_override("myfunc")(a);
else
return myif::myfunc(a);
}
};
BOOST_PYTHON_MODULE(myif){
class_<MyIfWrapper,boost::noncopyable>("myif")
.def("myfunc",&myif::myfunc)
;
}
// End answer by Eudoxos
int main( int argc, char ** argv ) {
try {
// Tell python that "myif" is a built-in module
PyImport_AppendInittab("myif", initmyif);
// Set up embedded Python interpreter:
Py_Initialize();
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");
PySys_SetPath(".");
main_namespace["mycl"] = import("mycl");
// Create the Python object with an eval()
object obj = eval("mycl.MyCl()", main_namespace);
// Find the base C++ type for the Python object (from Eudoxos)
const myif &b=extract<myif>(obj)();
std::cout << b.myfunc(5) << std::endl;
} catch( error_already_set ) {
PyErr_Print();
}
}
The key part that I've added here, above and beyond the "how do I embed Python using Boost.Python?" and "how do I extend Python using Boost.python?" (which was answered by Eudoxos) is the answer to the question "How do I do both at once in the same program?". The solution to this lies with the PyImport_AppendInittab call, which takes the initialisation function that would normally be called when the module is loaded and registers it as a built-in module. Thus when mycl.py says import myif it ends up importing the built-in Boost.Python module.
Take a look at Boost Python, that is the most versatile and powerful tool to bridge between C++ and Python.
http://www.boost.org/doc/libs/1_48_0/libs/python/doc/
There's no real way to interface C++ code directly with Python.
SWIG does handle this, but it builds its own wrapper.
One alternative I prefer over SWIG is ctypes, but to use this you need to create a C wrapper.
For the example:
// myif.h
class myif {
public:
virtual float myfunc(float a);
};
Build a C wrapper like so:
extern "C" __declspec(dllexport) float myif_myfunc(myif* m, float a) {
return m->myfunc(a);
}
Since you are building using C++, the extern "C" allows for C linkage so you can call it easily from your dll, and __declspec(dllexport) allows the function to be called from the dll.
In Python:
from ctypes import *
from os.path import dirname
dlldir = dirname(__file__) # this strips it to the directory only
dlldir.replace( '\\', '\\\\' ) # Replaces \ with \\ in dlldir
lib = cdll.LoadLibrary(dlldir+'\\myif.dll') # Loads from the full path to your module.
# Just an alias for the void pointer for your class
c_myif = c_void_p
# This tells Python how to interpret the return type and arguments
lib.myif_myfunc.argtypes = [ c_myif, c_float ]
lib.myif_myfunc.restype = c_float
class MyCl(myif):
def __init__:
# Assume you wrapped a constructor for myif in C
self.obj = lib.myif_newmyif(None)
def myfunc(a):
return lib.myif_myfunc(self.obj, a)
While SWIG does all this for you, there's little room for you to modify things as you please without getting frustrated at all the changes you have to redo when you regenerate the SWIG wrapper.
One issue with ctypes is that it doesn't handle STL structures, since it's made for C. SWIG does handle this for you, but you may be able to wrap it yourself in the C. It's up to you.
Here's the Python doc for ctypes:
http://docs.python.org/library/ctypes.html
Also, the built dll should be in the same folder as your Python interface (why wouldn't it be?).
I am curious though, why would you want to call Python from inside C++ instead of calling the C++ implementation directly?