├── .github └── workflows │ └── python-app.yml ├── .gitignore ├── CMakeLists.txt ├── io ├── input │ └── image.jpg └── output │ └── result.jpg ├── main.cpp ├── nlohmann └── json.hpp ├── readme.md ├── run.py ├── sajjad.cpp └── sajjad.h /.github/workflows/python-app.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Set up Python 3.10 23 | uses: actions/setup-python@v3 24 | with: 25 | python-version: "3.10" 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install flake8 pytest 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | - name: Lint with flake8 32 | run: | 33 | # stop the build if there are Python syntax errors or undefined names 34 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 35 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 36 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 37 | - name: Test with pytest 38 | run: | 39 | pytest 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.0) 2 | PROJECT(myapp) 3 | SET(CMAKE_CXX_STANDARD 17) 4 | SET(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | SET(CMAKE_CXX_EXTENSIONS OFF) 6 | 7 | # for run on other system 8 | SET(BUILD_SHARED_LIBS ON) 9 | SET(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) 10 | 11 | # sajjad library 12 | ADD_LIBRARY(sajjad SHARED sajjad.cpp) # for make .so library file 13 | ADD_EXECUTABLE(myapp main.cpp) 14 | TARGET_LINK_LIBRARIES(myapp sajjad) 15 | 16 | # opencv library 17 | FIND_PACKAGE(OpenCV REQUIRED) 18 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 19 | TARGET_LINK_LIBRARIES(sajjad ${OpenCV_LIBS}) 20 | -------------------------------------------------------------------------------- /io/input/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SajjadAemmi/Cpp-in-Python/56bf1119f5e687cd4bc8af59fcdcacc0db4415ca/io/input/image.jpg -------------------------------------------------------------------------------- /io/output/result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SajjadAemmi/Cpp-in-Python/56bf1119f5e687cd4bc8af59fcdcacc0db4415ca/io/output/result.jpg -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "nlohmann/json.hpp" 4 | #include "sajjad.cpp" 5 | 6 | using namespace std; 7 | using json = nlohmann::json; 8 | 9 | 10 | void test_json() 11 | { 12 | json j = { 13 | {"pi", M_PI}, 14 | {"happy", true}, 15 | {"name", "Sajjad"}, 16 | {"answer", { 17 | {"everything", 42} 18 | }}, 19 | {"list", {1, 0, 2}}, 20 | {"object", { 21 | {"currency", "USD"}, 22 | {"value", 42.99} 23 | }} 24 | }; 25 | 26 | cout << j.dump(4) << endl; 27 | } 28 | 29 | int main() 30 | { 31 | cout << "Welcome to this example" << endl; 32 | 33 | int x = 2; 34 | float y = 3.14; 35 | float z = add(x, y); 36 | cout << z << endl; 37 | 38 | int num = 2; 39 | cout << number2Word(num) << endl; 40 | 41 | char word[] = "one"; 42 | cout << word2Number(word) << endl; 43 | 44 | char input_image_path[] = "io/input/image.jpg"; 45 | char output_image_path[] = "io/output/result.jpg"; 46 | testOpenCV(input_image_path, output_image_path); 47 | 48 | test_json(); 49 | 50 | Sajjad obj; 51 | obj.testMethod(); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Cpp-in-Python 2 | 3 | [![Python application](https://github.com/SajjadAemmi/Cpp-in-Python/actions/workflows/python-app.yml/badge.svg)](https://github.com/SajjadAemmi/Cpp-in-Python/actions/workflows/python-app.yml) 4 | 5 | Run a C++ library in a Python script using ctypes 6 | 7 | ## installation 8 | Install opencv (linux ubuntu) 9 | ``` 10 | sudo apt install libopencv-dev 11 | ``` 12 | 13 | ## Build 14 | Compile C++ code to make `libsajjad.so` file in build directory using cmake or g++ 15 | 16 | ### build using cmake 17 | ``` 18 | cmake -S . -B build 19 | cmake --build build 20 | ``` 21 | 22 | ### build using g++ 23 | ``` 24 | mkdir build 25 | 26 | # for compile library 27 | g++ -fpic -shared sajjad.cpp -o build/libsajjad.so `pkg-config --libs --cflags opencv4` 28 | 29 | # for compile app 30 | g++ main.cpp -o build/myapp `pkg-config --libs --cflags opencv4` 31 | ``` 32 | 33 | ## Run 34 | Make sure C++ code compiled successfully via run the application 35 | ``` 36 | ./build/myapp 37 | ``` 38 | 39 | Finally ask Python to use compiled C++ library 40 | ``` 41 | python run.py 42 | ``` 43 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | 4 | libObject = ctypes.cdll.LoadLibrary('./build/libsajjad.so') 5 | 6 | # Test 1: send and receive numbers 7 | x = 2 8 | y = 3.14 9 | libObject.add.argtypes = [ctypes.c_int, ctypes.c_float] 10 | libObject.add.restype = ctypes.c_float 11 | z = libObject.add(x, y) 12 | print(z) 13 | 14 | # Test 2: send number and receive string 15 | num = 2 16 | libObject.number2Word.argtypes = [ctypes.c_int] 17 | libObject.number2Word.restype = ctypes.c_char_p 18 | word = libObject.number2Word(num) 19 | word = word.decode("utf-8") 20 | print(word) 21 | 22 | # Test 3: send string and receive number 23 | word = "one" 24 | word = word.encode() 25 | libObject.word2Number.argtypes = [ctypes.c_char_p] 26 | libObject.word2Number.restype = ctypes.c_int 27 | num = libObject.word2Number(word) 28 | print(num) 29 | 30 | # Test 4: work with opencv 31 | input_image_path = "io/input/image.jpg" 32 | input_image_path = input_image_path.encode() 33 | output_image_path = "io/output/result.jpg" 34 | output_image_path = output_image_path.encode() 35 | libObject.testOpenCV.argtypes = [ctypes.c_char_p, ctypes.c_char_p] 36 | libObject.testOpenCV(input_image_path, output_image_path) 37 | -------------------------------------------------------------------------------- /sajjad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sajjad.h" 6 | 7 | using namespace std; 8 | using namespace cv; 9 | 10 | 11 | Sajjad::Sajjad() 12 | { 13 | cout << "This is Sajjad constructor" << endl; 14 | } 15 | 16 | int Sajjad::testMethod() 17 | { 18 | cout << "This is Sajjad test method" << endl; 19 | return 0; 20 | } 21 | 22 | /* 23 | extern "C" required when using C++ compiler 24 | the extern "C" line ensures that your functions can be called from “C”. 25 | Because Python is written in C, your external function must be C callable. 26 | */ 27 | extern "C" 28 | { 29 | float add(int num1, float num2) 30 | { 31 | cout << "This is Sajjad add" << endl; 32 | return num1 + num2; 33 | } 34 | 35 | char* number2Word(int num) 36 | { 37 | char* word = new char[10]; 38 | if(num == 1) 39 | { 40 | strcpy(word, "one"); 41 | } 42 | else if(num == 2) 43 | { 44 | strcpy(word, "two"); 45 | } 46 | else 47 | { 48 | strcpy(word, "error"); 49 | } 50 | return word; 51 | } 52 | 53 | int word2Number(char word[]) 54 | { 55 | int num; 56 | if(strcmp(word, "one") == 0) 57 | { 58 | num = 1; 59 | } 60 | else if(strcmp(word, "two") == 0) 61 | { 62 | num = 2; 63 | } 64 | else 65 | { 66 | num = -1; 67 | } 68 | return num; 69 | } 70 | 71 | int testOpenCV(char input_image_path[], char output_image_path[]) 72 | { 73 | clock_t start_time, end_time; 74 | start_time = clock(); 75 | cv::Mat image_a, image_b, image_c; 76 | 77 | image_a = cv::imread(input_image_path, 1); 78 | if ( !image_a.data ) 79 | { 80 | cout << "No image data" << endl; 81 | return -1; 82 | } 83 | 84 | cv::resize(image_a, image_b, cv::Size(), 0.5, 0.5); 85 | cv::cvtColor(image_b, image_c, cv::COLOR_BGR2GRAY); 86 | 87 | cv::imwrite(output_image_path, image_c); 88 | // cv::namedWindow("Display Image", WINDOW_AUTOSIZE ); 89 | // cv::imshow("Display Image", image); 90 | // cv::waitKey(0); 91 | 92 | end_time = clock(); 93 | cout << "Result image saved in " << output_image_path << endl; 94 | cout << float(end_time - start_time) / CLOCKS_PER_SEC << " seconds" << endl; 95 | return 0; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /sajjad.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | 6 | class Sajjad 7 | { 8 | private: 9 | int x = 10; 10 | public: 11 | Sajjad(); 12 | int testMethod(); 13 | }; 14 | --------------------------------------------------------------------------------