diff --git a/CMakeLists.txt b/CMakeLists.txt index a1b00258..449b1267 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,5 +53,6 @@ endif() add_subdirectory(test) add_subdirectory(benchmark) add_subdirectory(examples) +add_subdirectory(python) install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 00000000..c59e3b44 --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,7 @@ +option(PYTHON_BINDINGS "Enable Python Bindings Compilation" OFF) +if(PYTHON_BINDINGS) + +add_subdirectory(bindings) + + +endif(PYTHON_BINDINGS) \ No newline at end of file diff --git a/python/bindings/CMakeLists.txt b/python/bindings/CMakeLists.txt new file mode 100644 index 00000000..73bf7f8d --- /dev/null +++ b/python/bindings/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.15) +project(CXXGraphPythonBindings LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Fetch pybind11 +include(FetchContent) +FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11.git + GIT_TAG v2.12.0 +) +FetchContent_MakeAvailable(pybind11) + + +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES /usr/local/lib ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}) +# Include headers +include_directories(${CMAKE_SOURCE_DIR}/include) + +# # Add library target (header-only here) +# add_library(CXXGraphLib INTERFACE) +# target_include_directories(CXXGraphLib INTERFACE ${CMAKE_SOURCE_DIR}/include) + +# Add pybind11 module +pybind11_add_module(cxxgraph bindings.cpp) diff --git a/python/bindings/bindings.cpp b/python/bindings/bindings.cpp new file mode 100644 index 00000000..33bc1810 --- /dev/null +++ b/python/bindings/bindings.cpp @@ -0,0 +1,53 @@ +#include // for std::function support +#include +#include +#include + +#include "CXXGraph/CXXGraph.hpp" + +namespace py = pybind11; + +PYBIND11_MODULE(cxxgraph, m) { + py::class_>(m, "Node") + .def(py::init(), py::arg("user_id"), + py::arg("data")) + .def(py::init(), py::arg("user_id"), + py::arg("data")) + + // Read-only property accessors + .def("get_id", &CXXGraph::Node::getId) + .def("get_user_id", &CXXGraph::Node::getUserId) + .def("get_data", static_cast::*)() const>( + &CXXGraph::Node::getData)) + .def("get_data_mut", static_cast::*)()>( + &CXXGraph::Node::getData)) + + // Mutator + .def("set_data", &CXXGraph::Node::setData) + + // Operators + .def(py::self == py::self) + .def(py::self < py::self) + + // String representation + .def("__repr__", [](const CXXGraph::Node &n) { + std::ostringstream oss; + oss << n; + return oss.str(); + }); + + py::class_>(m, "Edge").def( + py::init &, + const CXXGraph::Node &>()); // default ctor, + + py::class_>(m, "Graph") + .def(py::init<>()) // default ctor, no bool argument + .def("add_node", + static_cast::*)( + const CXXGraph::Node *)>(&CXXGraph::Graph::addNode)) + .def("add_edge", + static_cast::*)( + const CXXGraph::Edge *)>(&CXXGraph::Graph::addEdge)) + + .def("dfs", &CXXGraph::Graph::depth_first_search); +} diff --git a/python/tests/simpleTest.py b/python/tests/simpleTest.py new file mode 100644 index 00000000..47755763 --- /dev/null +++ b/python/tests/simpleTest.py @@ -0,0 +1,19 @@ +import sys +sys.path.append("../../build/python/bindings") + +import cxxgraph + +g = cxxgraph.Graph() +node1 = cxxgraph.Node("Node_1", 1) +node2 = cxxgraph.Node("Node_2", 2) +g.add_node(node1) +g.add_node(node1) +edge1 = cxxgraph.Edge(1,node1,node2) +g.add_edge(edge1) + + +result = g.dfs(node1) + +for node in result: + print(node.get_id(), node.get_user_id(), node.get_data()) +