-
Notifications
You must be signed in to change notification settings - Fork 0
simple fortran-cpp-python prototype #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| **/build | ||
| **/__pycache__/ | ||
| **/*.pyc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| cmake_minimum_required(VERSION 3.15) | ||
| project(F90_CPP_Python_Prototype LANGUAGES C CXX Fortran) | ||
|
|
||
| set(CMAKE_CXX_STANDARD 17) | ||
| set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
|
|
||
| # Find Python3 | ||
| find_package(Python3 REQUIRED COMPONENTS Interpreter Development) | ||
|
|
||
| message(STATUS "Python3 executable: ${Python3_EXECUTABLE}") | ||
| message(STATUS "Python3 include dirs: ${Python3_INCLUDE_DIRS}") | ||
| message(STATUS "Python3 libraries: ${Python3_LIBRARIES}") | ||
|
|
||
| # Find pybind11 | ||
| # First try to find pybind11 using CMake config | ||
| find_package(pybind11 CONFIG QUIET) | ||
|
|
||
| if(NOT pybind11_FOUND) | ||
| # If not found, try using Python to locate pybind11 | ||
| message(STATUS "pybind11 not found via CMake, trying Python...") | ||
| execute_process( | ||
| COMMAND ${Python3_EXECUTABLE} -c "import pybind11; print(pybind11.get_cmake_dir())" | ||
| OUTPUT_VARIABLE PYBIND11_CMAKE_DIR | ||
| OUTPUT_STRIP_TRAILING_WHITESPACE | ||
| RESULT_VARIABLE PYBIND11_PYTHON_RESULT | ||
| ) | ||
|
|
||
| if(PYBIND11_PYTHON_RESULT EQUAL 0) | ||
| message(STATUS "Found pybind11 via Python at: ${PYBIND11_CMAKE_DIR}") | ||
| set(pybind11_DIR ${PYBIND11_CMAKE_DIR}) | ||
| find_package(pybind11 CONFIG REQUIRED) | ||
| else() | ||
| message(FATAL_ERROR "pybind11 not found. Please install it: pip install pybind11") | ||
| endif() | ||
| endif() | ||
|
|
||
| message(STATUS "pybind11 version: ${pybind11_VERSION}") | ||
| message(STATUS "pybind11 includes: ${pybind11_INCLUDE_DIRS}") | ||
|
|
||
| # Add include directories | ||
| include_directories(${CMAKE_SOURCE_DIR}/src/cpp) | ||
|
|
||
| # ============================================ | ||
| # C++ Library (Python bridge) | ||
| # ============================================ | ||
| add_library(python_bridge STATIC | ||
| src/cpp/python_bridge.cpp | ||
| src/cpp/python_bridge.hpp | ||
| ) | ||
|
|
||
| target_include_directories(python_bridge PUBLIC | ||
| ${CMAKE_SOURCE_DIR}/src/cpp | ||
| ) | ||
|
|
||
| target_link_libraries(python_bridge | ||
| pybind11::embed | ||
| ) | ||
|
|
||
| # ============================================ | ||
| # Fortran Main Executable | ||
| # ============================================ | ||
| add_executable(fortran_main | ||
| src/fortran/fortran_main.f90 | ||
| ) | ||
|
|
||
| target_link_libraries(fortran_main | ||
| python_bridge | ||
| pybind11::embed | ||
| ) |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,73 @@ | ||||||||||||||
| #include "python_bridge.hpp" | ||||||||||||||
| #include <pybind11/pybind11.h> | ||||||||||||||
| #include <pybind11/embed.h> | ||||||||||||||
| #include <pybind11/numpy.h> | ||||||||||||||
| #include <iostream> | ||||||||||||||
|
|
||||||||||||||
| namespace py = pybind11; | ||||||||||||||
|
|
||||||||||||||
| // Single global: the Python interpreter | ||||||||||||||
| static std::unique_ptr<py::scoped_interpreter> interpreter; | ||||||||||||||
|
|
||||||||||||||
| extern "C" { | ||||||||||||||
|
|
||||||||||||||
| int initialize_python() { | ||||||||||||||
| std::cout << "[C++] Initializing Python interpreter..." << std::endl; | ||||||||||||||
|
|
||||||||||||||
| try { | ||||||||||||||
| interpreter = std::make_unique<py::scoped_interpreter>(); | ||||||||||||||
|
|
||||||||||||||
| py::module_ sys = py::module_::import("sys"); | ||||||||||||||
|
|
||||||||||||||
| std::string source_file = __FILE__; | ||||||||||||||
| auto this_dir = source_file.substr(0, source_file.find_last_of("/\\")); | ||||||||||||||
|
|
||||||||||||||
| std::cout << "[C++] Adding to sys.path: " << this_dir + "/../python" << std::endl; | ||||||||||||||
| sys.attr("path").attr("insert")(0, this_dir + "/../python"); | ||||||||||||||
|
|
||||||||||||||
| py::module_::import("python_main").attr("initialize")(); | ||||||||||||||
|
|
||||||||||||||
| std::cout << "[C++] Python initialized" << std::endl; | ||||||||||||||
| return 0; | ||||||||||||||
|
|
||||||||||||||
| } catch (const std::exception& e) { | ||||||||||||||
| std::cerr << "[C++] Error: " << e.what() << std::endl; | ||||||||||||||
| return -1; | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| int call_main_python(const float* data, int size, float* result) { | ||||||||||||||
| std::cout << "[C++] Calling call_main_python..." << std::endl; | ||||||||||||||
|
|
||||||||||||||
|
||||||||||||||
| if (result == nullptr) { | |
| std::cerr << "[C++] Error: result pointer is null." << std::endl; | |
| return -2; | |
| } |
Copilot
AI
Nov 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No validation is performed on the 'size' parameter before creating the array. If a negative or excessively large size is passed, it could lead to undefined behavior or security issues. Add validation to ensure size is positive and within reasonable bounds.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| #ifndef PYTHON_BRIDGE_HPP | ||
| #define PYTHON_BRIDGE_HPP | ||
|
|
||
| extern "C" { | ||
|
|
||
| int initialize_python(); | ||
|
|
||
| int call_main_python(const float* data, int size, float* result); | ||
|
|
||
| void finalize_python(); | ||
|
|
||
| } | ||
|
|
||
| #endif // PYTHON_BRIDGE_HPP |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| program fortran_main | ||
| use iso_c_binding | ||
| implicit none | ||
|
|
||
| ! Interface to C++ functions | ||
| interface | ||
| function initialize_python() bind(C, name="initialize_python") | ||
| use iso_c_binding | ||
| integer(c_int) :: initialize_python | ||
| end function initialize_python | ||
|
|
||
| function call_main_python(input_data, size, output_data) bind(C, name="call_main_python") | ||
| use iso_c_binding | ||
| real(c_float), dimension(*), intent(in) :: input_data | ||
| integer(c_int), value, intent(in) :: size | ||
| real(c_float), intent(out) :: output_data | ||
| integer(c_int) :: call_main_python | ||
| end function call_main_python | ||
|
|
||
| subroutine finalize_python() bind(C, name="finalize_python") | ||
| use iso_c_binding | ||
| end subroutine finalize_python | ||
| end interface | ||
|
|
||
| ! Local variables | ||
| integer(c_int), parameter :: array_size = 5 | ||
| integer(c_int) :: status | ||
| real(c_float), dimension(array_size) :: input_data | ||
| real(c_float) :: output_data | ||
| integer(c_int) :: i | ||
|
|
||
| print *, "========================================" | ||
| print *, "Fortran Main - Entry Point" | ||
| print *, "========================================" | ||
|
|
||
| ! Initialize input data | ||
| do i = 1, array_size | ||
| input_data(i) = real(i, kind=c_float) | ||
| end do | ||
|
|
||
| ! Initialize Python | ||
| print *, "" | ||
| print *, "[Fortran] Initializing..." | ||
| status = initialize_python() | ||
| if (status /= 0) then | ||
| print *, "[Fortran] ERROR: Initialization failed" | ||
| stop 1 | ||
| end if | ||
| print *, "[Fortran] Initialization successful" | ||
|
|
||
| ! Test call python operation | ||
| print *, "" | ||
| print *, "--- Testing Call Python Operation ---" | ||
| print *, "[Fortran] Sending data:" | ||
| do i = 1, array_size | ||
| print *, " data(", i, ") =", input_data(i) | ||
| end do | ||
|
|
||
| status = call_main_python(input_data, array_size, output_data) | ||
| if (status /= 0) then | ||
| print *, "[Fortran] ERROR: Operation failed" | ||
| else | ||
| print *, "[Fortran] Result:", output_data | ||
| end if | ||
|
|
||
| ! Cleanup | ||
| print *, "" | ||
| print *, "--- Cleanup ---" | ||
| call finalize_python() | ||
|
|
||
| print *, "" | ||
| print *, "========================================" | ||
| print *, "Fortran Main - Complete" | ||
| print *, "========================================" | ||
|
|
||
| end program fortran_main |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||
| """ | ||||||||||
| Python main is a lightweight entry point | ||||||||||
| """ | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def initialize(): | ||||||||||
| """ | ||||||||||
| Nothing to do here ... | ||||||||||
| """ | ||||||||||
| print("[Python] Dispatcher initialized") | ||||||||||
| return {"status": "ready"} | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def call_main_python(data, size): | ||||||||||
| """ | ||||||||||
| Dispatch to some torch calculation | ||||||||||
| """ | ||||||||||
| print(f"[Python] Dispatching tensor operation (size={size})") | ||||||||||
|
|
||||||||||
| # we are importing here so we don't need to make the | ||||||||||
| # cpp bridge aware of this stuff and such | ||||||||||
|
Comment on lines
+20
to
+21
|
||||||||||
| # we are importing here so we don't need to make the | |
| # cpp bridge aware of this stuff and such | |
| # We are importing here so we don't need to make the | |
| # C++ bridge aware of this stuff and such |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| """ | ||
| Some computations in pytorch | ||
| """ | ||
|
|
||
| import torch | ||
| import numpy as np | ||
|
|
||
| # Print torch info on module import | ||
| print(f"[Python] Module loaded - torch version: {torch.__version__}") | ||
| print(f"[Python] CUDA available: {torch.cuda.is_available()}") | ||
mahf708 marked this conversation as resolved.
Show resolved
Hide resolved
mahf708 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| def compute_tensor_operation(data, size): | ||
| """ | ||
| Perform a PyTorch tensor operation | ||
| """ | ||
| print(f"[Python] Computing tensor operation on {size} elements") | ||
|
|
||
| tensor = torch.from_numpy(np.array(data, dtype=np.float32)) | ||
|
|
||
| print(f"[Python] Input tensor: {tensor}") | ||
|
|
||
| result_value = torch.sum(tensor).item() | ||
|
|
||
| print(f"[Python] Result: {result_value}") | ||
|
|
||
| return result_value | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'data' pointer is not validated for null before being passed to py::array_t constructor. Add a null check to prevent potential crashes or undefined behavior.