rootwin
There are a few ways of embedding C++ into python. All of them requires cling. Unlike what many people believe, there exist a working C++ implementation that is able to execute C++ code by interpreting it. Namely, cling. Moreover, there is a C++ library called root that accesses this interpreter programmatically. Additionally, root has python bindings which support C++17 but they only work on linux. Also, there is a seperate library called cppyy which works on windows if you manage to win the fight against its broken build system. By the way, it claims to support C++20 by default, but in practice it only supports C++14 and there is no way of increasing this revision. Lastly there is this library, rootwin, which works on windows and also supports C++17 and it's not fake. However, it's interoperability capabilities are more limited compared to root and cppyy. Specifically, C++ symbols you define are not automatically wrapped into python objects and made available to python. Nonetheless, you can use some techniques which are explained below to get some amount of interoperability.
Installation
pip install rootwin
Python Api
rootwin.ProcessLine(code)
Execute the given C++ code (or cling metacommand). code can be a bytes literal. code will be converted to ctypes.c_char_p since this is actually a dll function. Unlike what the name indicates, code can contain multiple lines. The only reason why this name is chose is to emphesize that this function does nothing but calls the upstream C++ root library's ProcessLine() function.
rootwin.root_interface
This is the underlying ctypes.CDLL instance. Currently, no functions other than ProcessLine() are exported since the need haven't arised.
C++ Api
This is the api available to the C++ code executed by rootwin.ProcessLine().
template<typename T>
T py_to_pointer(const char *name, bool local = true)
Convert a python int to a C++ pointer. name is expected to be the name of a python int. Expected scope of this int is determined by the local argument. If local is true, name is searched in the local variables of the currently executing python function. Otherwise, it is searched in python globals.
Passing C++ objects to python
Unlike cppyy and root libraries, manual work is needed to access C++ objects from python. High level steps are below:
- Prepare the python world for the next steps by instantiating a ctypes.c_longlong. This will be our "out" argument.
- Take the address of this variable and assign it to another variable
- Call rootwin.ProcessLine(code) where code is a 2 liner trick which exports the address of any C++ object to python.
- In the first line, call py_to_pointer<long long*>("p2") where "p2" is the name of the python variable we created in the step 2.
- Next, dereference the pointer returned by py_to_pointer and assign the address of the C++ object you want to access from python to it.
- ctypes.c_longlong we created in the first step will be updated by the operation we performed on step 5.
- Convert the ctypes data type to an ordinary python int by accessing its value attribute.
- Use pyptrs in order to dereference the final int.
Example:
import ctypes
import rootwin
import pyptrs
pointer = ctypes.c_longlong(0)
pointer2 = ctypes.addressof(pointer)
rootwin.ProcessLine(b"int cpp_var = 100;")
rootwin.ProcessLine(b"""long long *pointer2 = py_to_pointer<long long*>("pointer2");""")
rootwin.ProcessLine(b"""*pointer2 = reinterpret_cast<long long>(&cpp_var);""")
cpp_pointer = pyptrs.pointer_to_address(pointer.value, ctype = ctypes.c_int)
cpp_var = pyptrs.dereference(cpp_pointer)
var = cpp_var.value
print(var) # prints 100
This is a rather simple example but by applying the same logic, you can also transfer more complex objects to python. This is possible since pyptrs supports pointers to custom ctypes structures.
Another example:
import ctypes
import rootwin
import pyptrs
pointer = ctypes.c_longlong(0)
pointer2 = ctypes.addressof(pointer)
code = b"""
struct A
{
int a = 10;
int b = 20;
int c = 30;
};
A cpp_object;
"""
rootwin.ProcessLine(code)
rootwin.ProcessLine(b"""long long *pointer2 = py_to_pointer<long long*>("pointer2");""")
rootwin.ProcessLine(b"""*pointer2 = reinterpret_cast<long long>(&cpp_object);""")
class A(ctypes.Structure):
_fields_ = [("a", ctypes.c_int),
("b", ctypes.c_int),
("c", ctypes.c_int)]
cpp_pointer = pyptrs.pointer_to_address(pointer.value, ctype = A)
cpp_object = pyptrs.dereference(cpp_pointer)
b = cpp_object.b
print(b)
Metadata
Release files for rootwin 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rootwin-1.0.0.tar.gz | 64.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rootwin-1.0.0-py2.py3-none-any.whl | Python 3, Python 2 | none | any | Details |
Total release size: 130.2 MB
Release files / rootwin-1.0.0.tar.gz
| Download URL | rootwin-1.0.0.tar.gz |
|---|---|
| Size | 64.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
57d282de82cd034f69deb51f326aef191bf30cc7a85725f5357a92f70a981974
|
|
BLAKE2b-256 checksum How to use checksums |
fee1f7a106b2939b0ee5271ee9886025512ff014017fff87e53d9562ae215a66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.0
|
Release files / rootwin-1.0.0-py2.py3-none-any.whl
| Download URL | rootwin-1.0.0-py2.py3-none-any.whl |
|---|---|
| Size | 66.3 MB |
| Tags | Python 2 Python 3 |
|
SHA-256 checksum How to use checksums |
52b74838a9f891ff2ef40eda5e1a32f8aaf5419e619f2e5aa6a75d40b1205e51
|
|
BLAKE2b-256 checksum How to use checksums |
a9b0827c95656574eab0cb1409d3fba86eb1fd2f4721d689ee35cf2acff1a96a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.0
|