Skip to content

Instantly share code, notes, and snippets.

@mmodenesi
Created August 31, 2019 23:40
Show Gist options
  • Save mmodenesi/021eb1ecf74257557b68311c7f130011 to your computer and use it in GitHub Desktop.
Save mmodenesi/021eb1ecf74257557b68311c7f130011 to your computer and use it in GitHub Desktop.
pybind use case example
#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include "someclass.h"
namespace py = pybind11;
void log(const std::string& msg) {
std::cout << "[C++ ] " << msg + '\n';
}
CppClass *user_defined_function(bool incref) {
py::module module = py::module::import("module");
py::object obj = module.attr("PyClass")();
if (incref) { obj.inc_ref(); }
CppClass* ret = obj.cast<CppClass *>();
log("end of user_defined_function");
return ret;
}
int main() {
py::scoped_interpreter guard{};
{
log("TEST 1: no factory function");
py::module module = py::module::import("module");
py::object obj = module.attr("PyClass")();
log("calling method on python object");
obj.attr("method")();
log("calling method on cpp pointer");
CppClass *instance1 = obj.cast<CppClass *>();
instance1->method();
log("end of scoped code");
}
std::cout << std::endl;
log("TEST 2: no incref");
CppClass* instance2 = user_defined_function(false);
// commented out to avoid segmentation fault
// log("calling method on cpp pointer");
// instance2->method();
// delete instance1;
std::cout << std::endl;
log("TEST 3 : incref");
CppClass* instance3 = user_defined_function(true);
log("calling method on cpp pointer");
instance3->method();
log("deleting instance3");
delete instance3;
std::cout << std::endl;
log("TEST 4 : incref and decref");
CppClass* instance4 = user_defined_function(true);
log("calling method on cpp pointer");
instance4->method();
log("decrementing ref count on instance4 wrapped python object");
py::object obj4 = py::cast(instance4);
obj4.dec_ref();
std::cout << std::endl;
log("TEST 5 : incref and decref twice");
CppClass* instance5 = user_defined_function(true);
log("calling method on cpp pointer");
instance5->method();
log("decrementing ref count on instance5 wrapped python object (twice)");
py::object obj5 = py::cast(instance5);
obj5.dec_ref();
obj5.dec_ref();
log("End of main");
return 0;
}
INCLUDES = $(shell python3 -m pybind11 --includes)
LIBS = -lpython3.6m
CCFLAGS = -O3 -Wall -shared -std=c++11 -fPIC
CC = g++
MODULE = someclass$(shell python3-config --extension-suffix)
TARGET = program
SOURCES = $(wildcard *.cc)
all: binding build
build: $(SOURCES)
@echo building $(TARGET)
$(CC) $(INCLUDES) $(SOURCES) -o $(TARGET) $(LIBS)
binding: someclass.cc
@echo building $(MODULE)
$(CC) $(CCFLAGS) $(INCLUDES) someclass.cc -o $(MODULE) $(LIBS)
clean:
rm -f $(TARGET) $(MODULE)
rm -rf __pycache__
.PHONY: clean all
from someclass import CppClass
def log(*msg):
print('[Python]', *msg)
class PyClass(CppClass):
_id = 0
@classmethod
def id(cls):
cls._id += 1
return cls._id
def __init__(self):
self._id = PyClass.id()
CppClass.__init__(self)
log("__init__ called on", self, self._id)
def method(self):
log("method called on", self, self._id)
def __del__(self):
log(" __del__ called on", self, self._id)
#include <iostream>
#include <pybind11/pybind11.h>
#include "someclass.h"
namespace py = pybind11;
void CppClass::method() {
std::cout << "[C++ ] method called [" << this << "]" << std::endl;
}
CppClass::~CppClass() {
std::cout << "[C++ ] destructor called [" << this << "]" << std::endl;
}
class Trampoline : public CppClass {
public:
using CppClass::CppClass;
void method() {
PYBIND11_OVERLOAD(
void,
CppClass,
method,
);
}
};
PYBIND11_MODULE(someclass, m) {
py::class_<CppClass, Trampoline>(m, "CppClass")
.def(py::init<>())
.def("method", &CppClass::method);
}
#ifndef __SOMECLASS_H
#define __SOMECLASS_H
#include <pybind11/pybind11.h>
namespace py = pybind11;
class CppClass {
public:
CppClass() = default;
virtual void method();
~CppClass();
};
#endif // __SOMECLASS_H
@sizmailov
Copy link

@mmodenesi I know it's just a minimal example to demonstrate the problem, and you are probably aware of the points I'm going to mention, but I'll still list them below in case if you are not.

  • use cmake for c++ project (it's de facto standard in c++ world nowadays)
  • separate c++ library code from python bindings

You may find this repo helpful

@mmodenesi
Copy link
Author

@sizmailov thank your for your answer on the main issue. I will probably spend a few days analyzing and undestanding the consequences before I comment back on the thread.

About make vs. cmake, I didn't know cmake was preferred. I am using make because the main application I am trying to extend uses make, so I kind of spent some time understanding make, and well... . Thank your very much for the tip, I'll definitely investigate cmake.

@sizmailov
Copy link

I see, you may prefer to stick to makefiles, it makes sense.

On the other hand it's often quite easy to write quite short CMakeLists.txt for small project with few makefiles... and can be contributed to main project.

I would recommend talk by Daniel Pfeifer to learn about cmake and it's best practices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment