Skip to content

reefactor/cppy3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cppy3

Embed Python 3 into your C++ app in 10 minutes

Minimalistic library for embedding CPython 3.x scripting language into C++ application

Lightweight simple and clean alternative to heavy boost.python. No additional dependencies. Crossplatform -- Linux, Windows platforms are supported.

cppy3 is sutable for embedding Python in C++ application while boost.python is evolved around extending Python with C++ module and it's embedding capabilities are somehow limited for now.

Features

  • Inject variables from C++ code into Python
  • Extract variables from Python to C++ layer
  • Reference-counted smart pointer wrapper for PyObject*
  • Manage Python init/shutdown with 1 line of code
  • Manage GIL with scoped lock/unlock guards
  • Forward exceptions (throw in Python, catch in C++ layer)
  • Nice C++ abstractions for Python native types list, dict and numpy.ndarray
  • Support Numpy ndarray via tiny C++ wrappers
  • Example interactive python console in 10 lines of code
  • Tested on Debian Linux x64 G++ and Mac OSX M1 Clang

Features examples code snippets from tests.cpp

Inject/extract variables C++ -> Python -> C++
// create interpreter cppy3::PythonVM instance; // inject cppy3::Main().injectVar<int>("a", 2); cppy3::Main().injectVar<int>("b", 2); cppy3::exec("assert a + b == 4"); cppy3::exec("print('sum is', a + b)"); // extract const cppy3::Var sum = cppy3::eval("a + b"); assert(sum.type() == cppy3::Var::LONG); assert(sum.toLong() == 4); assert(sum.toString() == L"4");
Forward exceptions Python -> C++
// create interpreter cppy3::PythonVM instance; try { // throw excepton in python cppy3::exec("raise Exception('test-exception')"); assert(false && "not supposed to be here"); } catch (const cppy3::PythonException& e) { // catch in c++ assert(e.info.type == L"<class 'Exception'>"); assert(e.info.reason == L"test-exception"); assert(e.info.trace.size() > 0); assert(std::string(e.what()).size() > 0); }

Support numpy ndarray

// create interpreter cppy3::PythonVM instance; cppy3::importNumpy(); // create numpy ndarray in C double cData[2] = {3.14, 42}; // create copy cppy3::NDArray<double> a(cData, 2, 1); // wrap cData without copying cppy3::NDArray<double> b; b.wrap(data, 2, 1); REQUIRE(a(1, 0) == cData[1]); REQUIRE(b(1, 0) == cData[1]); // inject into python __main__ namespace cppy3::Main().inject("a", a); cppy3::Main().inject("b", b); cppy3::exec("import numpy"); cppy3::exec("assert numpy.all(a == b), 'expect cData'"); // modify b from python (b is a shared ndarray over cData) cppy3::exec("b[0] = 100500"); assert(b(0, 0) == 100500); assert(cData[0] == 100500);

Scoped GIL Lock / Release management

// initially Python GIL is locked assert(cppy3::GILLocker::isLocked()); // add variable cppy3::exec("a = []"); cppy3::List a = cppy3::List(cppy3::lookupObject(cppy3::getMainModule(), L"a")); assert(a.size() == 0); // create thread that changes the variable a in a different thread const std::string threadScript = R"( import threading def thread_main():  global a  a.append(42)  t = threading.Thread(target=thread_main, daemon=True) t.start() )"; std::cout << threadScript << std::endl; cppy3::exec(threadScript); { // release GIL on this thread cppy3::ScopedGILRelease gilRelease; assert(!cppy3::GILLocker::isLocked()); // and wait thread changes the variable sleep(0.1F); { // lock GIL again before accessing python objects cppy3::GILLocker locker; assert(cppy3::GILLocker::isLocked()); // ensure that variable has been changed cppy3::exec("assert a == [42], a"); assert(a.size() == 1); assert((a[0]).toLong() == 42); } // GIL is released again assert(!cppy3::GILLocker::isLocked()); }

Requirements

  • C++11 compatible compiler (tested on GCC, Clang, MSVC)
  • CMake 3.12+
  • python3 3.5 dev package
  • numpy 1.x or 2.x (tested on numpy 1.26.4 and 2.4.0)

Build

Prerequisites
MacOSX

Brew python package has altogether dev headers and numpy included

sudo brew install cmake python3
Linux (Debian)
sudo apt-get install cmake g++ python3-dev

Numpy is very much desired but optional depending on cmake CPPY3_LINK_NUMPY option

sudo apt-get install python3-numpy
Windows

Cmake, Python with numpy is recommended.

Build

Testdrive

mkdir build cd build && cmake .. make ./tests/tests

Example interactive python console

mkdir build cd build && cmake .. make ./console

Release build

mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. cmake --build .

License

MIT License. Feel free to use

About

Embed Python into your C++ app in 10 minutes

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages