correct callback / overwrite pattern

master
heck 2 years ago
parent 9a579ad928
commit 0314cb5a2b
  1. 28
      README.md
  2. 48
      src/hello_world/__init__.py
  3. 19
      src/hello_world/native_hello_world/native_hello_world.cc
  4. 2
      src/hello_world/utils.py
  5. 31
      test/test.py

@ -1,22 +1,22 @@
# Boost Python "Hello World" Module
# Boost::Python "Hello World" Hybrid C++/Python Module
Make sure you do eveything in the python venv.
## VENV SETUP
Create a venv for your platform in the repo root called 'venv'.
Base interpreter: Python 3.7.5
## BUILD AND INSTALL
## BUILD
``` bash
source venv/bin/activate
setup.py build_ext install
make
```
## TEST
``` bash
make devshell
make testenv
cd test
./test.py
```
```
### make targets
* make devshell - gives you shell with setup env vars
* PYTHONPATH
* LD_LIBRARY_PATH
* make testenv - executes a python interpreter importing the module

@ -11,39 +11,59 @@ from native_hello_world import *
# TODO: inter-pkg ref to make sure which native_pEp in sys.path gets loaded
# like: pEp.native_pEp
# import the module (you can use all the names, including the ones beginning with underscore)
# import the module
# so you can use all the names from the native module, including the ones beginning with underscore)
import native_hello_world
from .utils import log_call
# Init sequence of the module on 'import hello_world'
# 1. init_before_main_module() (native)
# 2. hello_world.init() (python) (this function)
# 3. _init_after_main_module() (native)
@log_call
def init():
# do some python init here
native_hello_world._init_after_main_module()
# Calls (wraps) a C++ function
# there is a c++ function _wrapped_cxx_function() which
# wont be imported into this module because of the underscore
# so you can wrap it in python and hide it
# in the wrapped you can augment/change the behaviour
@log_call
def wrapped_cxx_function():
return not native_hello_world._wrapped_cxx_function()
# Gets called from C++
# use hello_world.call_python_func()
# A pure python function
# we can freely combine python and c++ code
@log_call
def pure_python_func():
msg = "a string from python"
def pure_python_func(number, string):
print("Default implementation of callback")
msg = "a string from python made from params n:{}, s:{}".format(number, string)
return msg
# Init sequence on 'import hello_world'
# 1. init_before_main_module() (native)
# 2. hello_world.init() (python)
# 3. _init_after_main_module() (native)
# Example callback
# This function is as a callback target for a
# function call from C++.
# use hello_world.trigger_callback()
@log_call
def init():
# do some python init here
# then execute some native init
native_hello_world._init_after_main_module()
def callback_func(number, string):
print("Default implementation of callback")
msg = "a string from python made from params n:{}, s:{}".format(number, string)
return msg
# Executed when run as script
@log_call
def main():
print("I am being run as a script")
# MAIN
if __name__ == "__main__":
main()

@ -10,7 +10,6 @@ using namespace boost::python;
// All functions (global) that will be wrapped and not exposed directly need to begin with an underscore
// All symbols not beginning with an underscore will be visible in hello_world
object hello_world_pyobj;
void init_before_main_module() {
cout << "init_before_main_module() - called" << endl;
@ -19,9 +18,6 @@ void init_before_main_module() {
// hidden init function, wrapped by hello_world.init()
void _init_after_main_module() {
cout << "_init_after_main_module() - called" << endl;
// import the main module 'hello_world'
hello_world_pyobj = import("hello_world");
assert(hello_world_pyobj);
}
string cxx_function() {
@ -37,12 +33,15 @@ bool _wrapped_cxx_function() {
return true;
}
void call_python_func() {
cout << "call_python_func() called" << endl;
object func = hello_world_pyobj.attr("pure_python_func");
string msg = call<string>(func.ptr());
string trigger_callback() {
cout << "trigger_callback() called" << endl;
// Cant be global, because of the dynamic nature of python
// And the "func pointer" can be changed anytime (callback override)
object module_ref = import("hello_world");
object funcref = module_ref.attr("callback_func");
string msg = call<string>(funcref.ptr(), 23, "c++");
cout << msg << endl;
// return msg.c_str();
return msg.c_str();
}
struct World {
@ -65,7 +64,7 @@ BOOST_PYTHON_MODULE (native_hello_world) {
def("_init_after_main_module", &_init_after_main_module);
def("cxx_function", &cxx_function);
def("_wrapped_cxx_function", &_wrapped_cxx_function);
def("call_python_func", &call_python_func);
def("trigger_callback", &trigger_callback);
class_<World>("World")
.def("cxx_method", &World::cxx_method)

@ -2,6 +2,6 @@
# a decorator to log all function calls
def log_call(func):
def wrapper(*args, **kwargs):
print("{}.{} - called".format(func.__module__,func.__name__))
print("{}.{}() - called".format(func.__module__,func.__name__))
return func(*args, **kwargs)
return wrapper

@ -6,22 +6,41 @@ def test_cxx_module():
print('\nPrint symbols of C++ module (native_hello_world)')
import native_hello_world
print('native_hello_world', dir(native_hello_world))
print('\nExecute some C++ test functions directly')
print('\nTest a function from the native_hello_world module directly')
native_hello_world._wrapped_cxx_function()
# Callback function
def user_callback_func(number, string):
print("callback user implementation");
msg = "USER IMPL: n={}, s={}".format(number, string)
return msg
test_cxx_module()
print('\nPrint global symbols of hybrid module (hello_world)')
print('hello_world', dir(hello_world))
print('\nExecute some global test functions')
hello_world.pure_python_func()
print('\nTest the pure python fumction')
hello_world.pure_python_func(43, "python")
print('\nTest the c++ function')
hello_world.cxx_function()
print('\nTest the wrapped c++ function')
hello_world.wrapped_cxx_function()
print('\nTest callback (python calling a c++ function)')
hello_world.trigger_callback();
print('\nTest Overwriting the callback')
# overwrite the default callback
hello_world.callback_func = user_callback_func
hello_world.trigger_callback();
print('\nPrint symbols of C++ class in hybrid module (hello_world)')
w = hello_world.World()
print(dir(w))
# print('\nPrint symbols of C++ class in hybrid module (hello_world)')
# w = hello_world.World()
# print(dir(w))
# print('\nExecute some member methods')
# w.cxx_method()

Loading…
Cancel
Save