Skip to content

Commit 94882a7

Browse files
committed
feat(python): add Python bindings for C++ modules
1 parent 8baad10 commit 94882a7

20 files changed

+3108
-1
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
2222
# Options
2323
option(BUILD_TESTS "Build tests" ${IS_MAIN_PROJECT})
2424
option(BUILD_EXAMPLES "Build examples" ${IS_MAIN_PROJECT})
25+
option(BUILD_PYTHON_BINDINGS "Build Python bindings" OFF)
2526
option(ENABLE_WARNINGS "Enable compiler warnings" ON)
2627

2728
# macOS-specific fixes for Mach-O linker errors
@@ -117,6 +118,11 @@ if(BUILD_EXAMPLES)
117118
add_subdirectory(examples)
118119
endif()
119120

121+
# Build Python bindings if enabled
122+
if(BUILD_PYTHON_BINDINGS)
123+
add_subdirectory(binding)
124+
endif()
125+
120126
# Installation
121127
include(GNUInstallDirs)
122128
include(CMakePackageConfigHelpers)

README.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ This project serves as both a learning resource and a reference implementation f
3030
- **Type Safety**: Concept-based constraints preventing common programming errors
3131
- **Performance Tools**: Built-in timing utilities and benchmark framework
3232
- **Error Handling**: Multiple error handling strategies (exceptions, `Result` type, `std::expected`)
33+
- **Python Bindings**: Complete pybind11 integration with modern Python 3.13 features
3334

3435
### Code Quality & Development
3536

@@ -92,6 +93,7 @@ This project demonstrates practical applications of:
9293
- **C++23 compatible compiler** ([Clang] 20+ / [GCC] 14+)
9394
- **[CMake] 3.23+**
9495
- **[Ninja] build system** (required for CMake - faster builds than Make)
96+
- **Python 3.13+** (optional, for Python bindings)
9597

9698
It's recommended to use a development container for the best development experience.
9799
See [`.devcontainer/README.md`](.devcontainer/README.md) for more details.
@@ -134,6 +136,13 @@ cmake --build --preset release
134136
ctest --preset release
135137
```
136138

139+
#### Build Python bindings (optional)
140+
141+
```bash
142+
cmake --preset release -DBUILD_PYTHON_BINDINGS=ON
143+
cmake --build --preset release
144+
```
145+
137146
### CMake Presets
138147

139148
This project uses CMake presets for streamlined build configuration.
@@ -189,7 +198,14 @@ cmake --workflow --preset release-workflow # Complete release cycle
189198

190199
### Build Options
191200

192-
See [`cmake/README.md`](cmake/README.md#options) for available build options.
201+
| Option | Description | Default |
202+
|-------------------------|------------------------------------|-----------------------|
203+
| `BUILD_TESTS` | Build test suite | `ON` (main project) |
204+
| `BUILD_EXAMPLES` | Build example applications | `ON` (main project) |
205+
| `BUILD_PYTHON_BINDINGS`| Build Python bindings with pybind11| `OFF` |
206+
| `ENABLE_WARNINGS` | Enable compiler warnings | `ON` |
207+
208+
See [`cmake/README.md`](cmake/README.md#options) for additional build options.
193209

194210
### Pre-commit Setup (Recommended)
195211

@@ -256,6 +272,33 @@ auto main() -> int {
256272
}
257273
```
258274

275+
### Python Usage
276+
277+
```python
278+
from cpp_features import shapes, containers, algorithms
279+
280+
# Create and use shapes
281+
circle = shapes.create_shape("circle", 5.0)
282+
rectangle = shapes.create_shape("rectangle", 4.0, 3.0)
283+
284+
print(f"Circle area: {circle.get_area():.2f}")
285+
print(f"Rectangle area: {rectangle.get_area():.2f}")
286+
287+
# Use enhanced containers
288+
container = containers.create_container([1, 3, 2, 5, 4])
289+
algorithms.sort_inplace(container)
290+
print(f"Sorted: {list(container)}")
291+
292+
# Functional programming with modern Python features
293+
from cpp_features.algorithms import functional_chain
294+
295+
result = (functional_chain([1, 2, 3, 4, 5, 6])
296+
.filter(lambda x: x % 2 == 0)
297+
.map(lambda x: x * x)
298+
.collect())
299+
print(f"Even squares: {result}") # [4, 16, 36]
300+
```
301+
259302
## 📁 Project Structure
260303

261304
```text
@@ -266,6 +309,16 @@ cpp-demo-project/
266309
│ ├── launch.json # VS Code launch configuration
267310
│ ├── settings.json # VS Code settings
268311
│ └── tasks.json # VS Code tasks
312+
├── binding/ # Python bindings using pybind11
313+
│ ├── CMakeLists.txt # Python bindings build configuration
314+
│ ├── cpp_features.cpp # Main pybind11 module
315+
│ ├── shapes_binding.cpp # Shapes module bindings
316+
│ ├── containers_binding.cpp # Containers module bindings
317+
│ ├── algorithms_binding.cpp # Algorithms module bindings
318+
│ ├── exceptions_binding.cpp # Exceptions module bindings
319+
│ ├── memory_binding.cpp # Memory module bindings
320+
│ ├── timing_binding.cpp # Timing module bindings
321+
│ └── random_binding.cpp # Random module bindings
269322
├── build/ # Build output (generated by CMake)
270323
│ ├── debug/ # Debug build output
271324
│ ├── release/ # Release build output
@@ -286,6 +339,15 @@ cpp-demo-project/
286339
│ ├── random/ # Type-safe random number generation
287340
│ ├── shapes/ # Polymorphic shapes with factory functions
288341
│ └── timing/ # Performance measurement and benchmarking
342+
├── python/ # Python wrapper modules
343+
│ ├── __init__.py # Package initialization
344+
│ ├── shapes.py # Enhanced shapes module with Python 3.13 features
345+
│ ├── containers.py # Enhanced containers module
346+
│ ├── algorithms.py # Functional programming utilities
347+
│ ├── exceptions.py # Result types and error handling
348+
│ ├── memory.py # RAII and resource management
349+
│ ├── timing.py # Benchmarking and timing utilities
350+
│ └── random.py # Enhanced random number generation
289351
├── src/ # Source implementation files
290352
│ ├── CMakeLists.txt # Components configuration
291353
│ └── [mirrors include structure]

binding/CMakeLists.txt

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Python bindings for cpp-demo-project
2+
# This directory contains pybind11 bindings for all C++ modules
3+
4+
cmake_minimum_required(VERSION 3.23)
5+
6+
# Ensure pybind11 is available
7+
if(NOT TARGET pybind11::pybind11)
8+
message(
9+
FATAL_ERROR
10+
"pybind11 not found. Enable BUILD_PYTHON_BINDINGS and ensure pybind11 is available."
11+
)
12+
endif()
13+
14+
# Create the main Python module
15+
pybind11_add_module(cpp_features
16+
cpp_features.cpp
17+
shapes_binding.cpp
18+
containers_binding.cpp
19+
algorithms_binding.cpp
20+
exceptions_binding.cpp
21+
memory_binding.cpp
22+
timing_binding.cpp
23+
random_binding.cpp
24+
)
25+
26+
# Link against all the required libraries
27+
target_link_libraries(cpp_features PRIVATE demo::lib)
28+
29+
# Include directories
30+
target_include_directories(
31+
cpp_features
32+
PRIVATE
33+
${CMAKE_SOURCE_DIR}/include
34+
${CMAKE_SOURCE_DIR}/src
35+
)
36+
37+
# Set properties for the Python module
38+
set_target_properties(
39+
cpp_features
40+
PROPERTIES
41+
CXX_STANDARD
42+
23
43+
CXX_STANDARD_REQUIRED
44+
ON
45+
CXX_VISIBILITY_PRESET
46+
hidden
47+
VISIBILITY_INLINES_HIDDEN
48+
ON
49+
)
50+
51+
# Compile definitions
52+
target_compile_definitions(cpp_features PRIVATE VERSION_INFO=${PROJECT_VERSION})
53+
54+
# Install the Python module
55+
install(TARGETS cpp_features COMPONENT python LIBRARY DESTINATION .)

binding/algorithms_binding.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* @file algorithms_binding.cpp
3+
* @brief Python bindings for the algorithms module
4+
*/
5+
6+
#include <pybind11/functional.h>
7+
#include <pybind11/pybind11.h>
8+
#include <pybind11/stl.h>
9+
10+
#include "algorithms/stl.hpp"
11+
#include "containers/container.hpp"
12+
13+
namespace py = pybind11;
14+
15+
void bind_algorithms(py::module &m) {
16+
using namespace cpp_features::algorithms;
17+
using namespace cpp_features::containers;
18+
19+
// Bind sort functions
20+
m.def(
21+
"sort_container", [](Container<int> &container) { SortContainer(container); },
22+
"Sort an integer container");
23+
24+
m.def(
25+
"sort_container", [](Container<double> &container) { SortContainer(container); },
26+
"Sort a double container");
27+
28+
m.def(
29+
"sort_container", [](Container<std::string> &container) { SortContainer(container); },
30+
"Sort a string container");
31+
32+
m.def(
33+
"sort_container", [](std::vector<int> &container) { SortContainer(container); },
34+
"Sort an integer vector");
35+
36+
m.def(
37+
"sort_container", [](std::vector<double> &container) { SortContainer(container); },
38+
"Sort a double vector");
39+
40+
m.def(
41+
"sort_container", [](std::vector<std::string> &container) { SortContainer(container); },
42+
"Sort a string vector");
43+
44+
// Bind count_if functions
45+
m.def(
46+
"count_if",
47+
[](const std::vector<int> &range, std::function<bool(int)> predicate) {
48+
return CountIf(range, predicate);
49+
},
50+
"Count elements matching predicate in integer vector");
51+
52+
m.def(
53+
"count_if",
54+
[](const std::vector<double> &range, std::function<bool(double)> predicate) {
55+
return CountIf(range, predicate);
56+
},
57+
"Count elements matching predicate in double vector");
58+
59+
m.def(
60+
"count_if",
61+
[](const std::vector<std::string> &range,
62+
std::function<bool(const std::string &)> predicate) { return CountIf(range, predicate); },
63+
"Count elements matching predicate in string vector");
64+
65+
m.def(
66+
"count_if",
67+
[](const Container<int> &range, std::function<bool(int)> predicate) {
68+
return CountIf(range, predicate);
69+
},
70+
"Count elements matching predicate in integer container");
71+
72+
m.def(
73+
"count_if",
74+
[](const Container<double> &range, std::function<bool(double)> predicate) {
75+
return CountIf(range, predicate);
76+
},
77+
"Count elements matching predicate in double container");
78+
79+
// Bind transform functions
80+
m.def(
81+
"transform_to_vector",
82+
[](const std::vector<int> &range, std::function<int(int)> transform) {
83+
return TransformToVector(range, transform);
84+
},
85+
"Transform integer vector to new vector");
86+
87+
m.def(
88+
"transform_to_vector",
89+
[](const std::vector<double> &range, std::function<double(double)> transform) {
90+
return TransformToVector(range, transform);
91+
},
92+
"Transform double vector to new vector");
93+
94+
m.def(
95+
"transform_to_vector",
96+
[](const std::vector<int> &range, std::function<double(int)> transform) {
97+
return TransformToVector(range, transform);
98+
},
99+
"Transform integer vector to double vector");
100+
101+
m.def(
102+
"transform_to_vector",
103+
[](const Container<int> &range, std::function<int(int)> transform) {
104+
return TransformToVector(range, transform);
105+
},
106+
"Transform integer container to vector");
107+
108+
m.def(
109+
"transform_to_vector",
110+
[](const Container<double> &range, std::function<double(double)> transform) {
111+
return TransformToVector(range, transform);
112+
},
113+
"Transform double container to vector");
114+
115+
// Bind find_min_max functions
116+
m.def(
117+
"find_min_max", [](const std::vector<int> &range) { return FindMinMax(range); },
118+
"Find minimum and maximum in integer vector");
119+
120+
m.def(
121+
"find_min_max", [](const std::vector<double> &range) { return FindMinMax(range); },
122+
"Find minimum and maximum in double vector");
123+
124+
m.def(
125+
"find_min_max", [](const Container<int> &range) { return FindMinMax(range); },
126+
"Find minimum and maximum in integer container");
127+
128+
m.def(
129+
"find_min_max", [](const Container<double> &range) { return FindMinMax(range); },
130+
"Find minimum and maximum in double container");
131+
}

0 commit comments

Comments
 (0)