https://github.com/paulross/pycppcontainers
Converting homogeneous containers between C++ and Python.
https://github.com/paulross/pycppcontainers
Last synced: about 2 months ago
JSON representation
Converting homogeneous containers between C++ and Python.
- Host: GitHub
- URL: https://github.com/paulross/pycppcontainers
- Owner: paulross
- License: mit
- Created: 2022-02-15T09:21:25.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2024-10-24T10:21:13.000Z (7 months ago)
- Last Synced: 2025-03-24T03:03:31.564Z (2 months ago)
- Language: C++
- Size: 33.8 MB
- Stars: 2
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: HISTORY.md
- License: LICENSE
Awesome Lists containing this project
README
# Python and C++ Containers
Python is well known for it's ability to handle *heterogeneous* data in containers such as lists.
But what if you need to convert to and from C++ containers such as ``std::vector`` that require *homogeneous* data types?This C++ project is about converting between C++ containers and Python's (`tuple` ``list``, ``set``, ``frozenset``, ``dict``) containing
homogeneous types (``bool``, ``int``, ``float``, ``complex``, ``bytes``, ``str``) to and from their C++ equivalents.Two-way conversion for this set of containers:
| C++ Container | Python Equivalent |
|----------------------------|-------------------|
| `std::vector` | `list` |
| `std::vector` | `tuple` |
| `std::list` | `list` |
| `std::list` | `tuple` |
| `std::unordered_set` | `set` |
| `std::unordered_set` | `frozenset` |
| `std::unordered_map` | `dict` |
| `std::map` | `dict` |Containing these objects:
| C++ Type | Python Equivalent |
|--------------------------|---------------------|
| ``bool`` | ``True``, ``False`` |
| ``long`` | ``int`` |
| ``double`` | ``float`` |
| ``std::complex`` | ``complex`` |
| ``std::vector`` | ``bytes`` |
| ``std::string`` | ``str`` |These combinations would normally need 216 specific conversion
functions.This project reduces that to just **six** hand maintained functions.
The 216 actual conversion functions are then created automatically
using a mixture of templates, partial specialisation and code generation.
This approach means that new types and containers can be added with ease.## Using This Library
### C++ To Python
TODO:
Suppose that you have a Python list of floats that needs to be passed to a C++ function that expects `std::vector`.
Then that C++ function modifies that vector and you need the result as a new Python list of floats.
With this library your code will be as simple as this:```c++
static PyObject *
your_function_name(void) {
std::vector container = some_cpp_function_that_creates_a_vector();
// Convert the vector back to a new Python list of float
// with a generic function.
return cpp_std_list_like_to_py_list(container);
}
```Some other variations, firstly create a Python `tuple` rather than a `list`:
```c++
static PyObject *
your_function_name(void) {
std::vector container = some_cpp_function_that_creates_a_vector();
return cpp_std_list_like_to_py_tuple(container);
}
```Or work with a `std::list` rather than a `std::vector`:
```c++
static PyObject *
your_function_name(void) {
std::list container = some_cpp_function_that_creates_a_list();
return cpp_std_list_like_to_py_list(container);
}
```Or work with a `std::vector>`:
```c++
static PyObject *
your_function_name(void) {
std::vector container = some_cpp_function_that_creates_a_vector();
return cpp_std_list_like_to_py_list(container);
}
```Note `cpp_std_list_like_to_py_list(container)` will select the correct type conversion or will give
a compile time error if there is a type mismatch.### Python to C++
```c++
static PyObject *
your_function_name(PyObject *arg) {
// Declare the specific vector type
std::vector vec;
// Call the generic function to convert a list to a std::vector.
// This returns non-zero if it can not convert arg to a
// std::vector
if (!py_list_to_cpp_std_list_like(arg, vec)) {
// Send the std::vector to the C++ library.
// ...
Py_RETURN_NONE;
}
return NULL;
}
```# Usage
## Code Generation
If necessary run the code generator:
```shell
cd src/py
python code_gen.py
```Which should give you something like:
```shell
venv/bin/python src/py/code_gen.py
Target directory "src/cpy"
Writing declarations to "src/cpy/auto_py_convert_internal.h"
Wrote 2654 lines of code with 220 declarations.
Writing definitions to "src/cpy/auto_py_convert_internal.cpp"
Wrote 2384 lines of code with 216 definitions.Process finished with exit code 0
```## C++ Build Configuration
You need to compile the following C++ files by adding them to your makefile or CMakeLists.txt:
```text
src/cpy/auto_py_convert_internal.cpp
src/cpy/python_container_convert.cpp
src/cpy/python_object_convert.cpp
```### Source Inclusion
Your pre-processor needs access to the header files with the compiler flag ``-I src/cpy``.
Then in your C++ code include:
```c++
#include "python_convert.h"
```Which gives you access to the whole API in the namespace ``Python_Cpp_Containers``.
## Python Extension Example
There are some examples of using this library in *src/ext/cPyCppContainers.cpp*.
This extension is built by *setup.py* and tested with *tests/unit/test_cPyCppContainers.py*.To build this extension:
```shell
$ python setup.py develop
```And to use it:
```python
import cPyCppContainer
```There are a number of functions there that exploit this C++ library.
For example this C function create a C++ ``std::vector`` from a Python list of
floats then creates a new Python list of floats from that C++ container ('round-tripping').```c++
static PyObject *
new_list_float(PyObject *arg) {
std::vector vec;
if (!py_list_to_cpp_std_list_like(arg, vec)) {
return cpp_std_list_like_to_py_list(vec);
}
return NULL;
}
```This can be called from Python thus:
```python
import cPyCppContainers
cPyCppContainers.new_list_float([1.0, 2.0])
[1.0, 2.0]
```If the Python list contains non-floats an exception will be raised:
```python
cPyCppContainers.new_list_float([1.0, 2])
Traceback (most recent call last):
File "", line 1, in
ValueError: Python value of type int can not be converted
```### Testing
To test the cPyCppContainers extension which exercises much of the C++ code:
```shell
$ pytest tests/
```This takes a few seconds.
There are a couple of options that can be added:* ``--runslow`` will run slow tests including performance test. Use the ``-s`` option to obtain the performance output.
* ``--pymemtrace`` will run memory tracing tests. This requires [pymemtrace](https://pypi.org/project/pymemtrace/) to be installed.So for the full set of tests:
``` shell
$ pytest -vs --runslow --pymemtrace tests/
```This can take around 30 minutes to complete.
# Credits
Many thanks to my employer [Paige](https://www.paige.ai) for letting me opensource this project.