diff --git a/README.md b/README.md index 9fc3ebb..bb34cc7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ -# python-calling-c-cpp +To list every symbol in a dll, use "nm". -Example of calling C and C++ functions in Python \ No newline at end of file +"nm -g hello.so" output "T square", where hello.so is created from a **C** source file containing function square. +"nm -g callee.so" output "T \_Z7isprimex", "_Z9sumprimesx", where callee.so is created from a **C++** file containing function isprime and sumprimes. + +Why does the names differ? C++ compilers use "name mangling" to support overloading, etc, resulting in names that differ from names in the source file. This is not done in C compilers (since C does not support overloading as well). + +To prevent name mangling, use `extern "C"` directive in the function declaration. +The extern keyword may be applied to a global variable, function, or template declaration. It specifies that the symbol has external linkage. +`extern "C"` specifies that the function is defined elsewhere and uses the C-language calling convention, which includes do not mangle names. \ No newline at end of file diff --git a/calling-c/Makefile b/calling-c/Makefile new file mode 100644 index 0000000..acbb04c --- /dev/null +++ b/calling-c/Makefile @@ -0,0 +1,8 @@ +hello.so: hello.o + gcc -shared -o hello.so hello.o + +hello.o: hello.c + gcc -std=c99 -c -o hello.o hello.c + +clean: + rm hello.o hello.so \ No newline at end of file diff --git a/calling-c/caller.py b/calling-c/caller.py new file mode 100644 index 0000000..8550e2a --- /dev/null +++ b/calling-c/caller.py @@ -0,0 +1,43 @@ +import ctypes +hello_lib = ctypes.CDLL('./hello.so') #/windows + +# 1. Square (int -> int) +hello_lib.square.argtypes = [ctypes.c_int] +hello_lib.square.restype = ctypes.c_int + +x = 4 +result = hello_lib.square(x) +print(f'the square of {x} is {result}') + +hello_lib.sum_elements.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.c_int] +hello_lib.sum_elements.restype = ctypes.c_int + +# 2. Sum (int*, int -> int) +N = 4 +p = [3, 1, 2, 4] +# Converts Python list to C array. (*p) is equivalent to (3, 1, 2, 4) +c_array = (ctypes.c_int * len(p))(*p) +# Get the address of the array +p_pointer = ctypes.cast(c_array, ctypes.POINTER(ctypes.c_int)) + +result = hello_lib.sum_elements(p_pointer, N) # pass in p_pointer or c_array both works +print(f"the sum of the array is {result}") + +import numpy as np +from numpy.ctypeslib import ndpointer + +# 3. Sort (in place float*) +hello_lib.bubble_sort.argtypes = [ndpointer(dtype=np.float32, flags='C_CONTIGUOUS'), ctypes.c_int] +hello_lib.bubble_sort.restype = None +arr = np.array([3.0, 2.3, 4.2, 1.9, 3.2, 1.7], dtype=np.float32) +hello_lib.bubble_sort(arr, len(arr)) +print(arr) + +# 4. Create (int, float -> float*) +a = 0.523 +N = 6 +hello_lib.generate_sine_values.argtypes = [ctypes.c_float, ctypes.c_int] +hello_lib.generate_sine_values.restype = ndpointer(dtype=np.float32, shape=(N,)) +result_arr = hello_lib.generate_sine_values(a, N) +print(type(result_arr)) +print(result_arr) \ No newline at end of file diff --git a/calling-c/hello.c b/calling-c/hello.c new file mode 100644 index 0000000..bcacdb1 --- /dev/null +++ b/calling-c/hello.c @@ -0,0 +1,34 @@ +#include +#include + +int square(int x) { + return x*x; +} + +int sum_elements(int *p, int N) { + int sum = 0; + for (int i = 0; i < N; i++) { + sum += p[i]; + } + return sum; +} + +void bubble_sort(float *p, int N){ + for (int i=0;ip[j+1]){ + float temp = p[j]; + p[j] = p[j+1]; + p[j+1] = temp; + } + } + } +} + +float *generate_sine_values(float a, int N) { + float *result = malloc(N * sizeof(float)); + for (int k = 0; k < N; ++k) { + result[k] = sin(k * a); + } + return result; +} \ No newline at end of file diff --git a/calling-c/hello.o b/calling-c/hello.o new file mode 100644 index 0000000..65e4b51 Binary files /dev/null and b/calling-c/hello.o differ diff --git a/calling-c/hello.so b/calling-c/hello.so new file mode 100644 index 0000000..03a9a68 Binary files /dev/null and b/calling-c/hello.so differ diff --git a/calling-cpp/Makefile b/calling-cpp/Makefile new file mode 100644 index 0000000..5098a8d --- /dev/null +++ b/calling-cpp/Makefile @@ -0,0 +1,8 @@ +callee.o: workload.cpp workload.h + g++ -c workload.cpp -o callee.o + +callee.so: callee.o + g++ -shared -o callee.so callee.o + +clean: + rm callee.o callee.so \ No newline at end of file diff --git a/calling-cpp/callee.o b/calling-cpp/callee.o new file mode 100644 index 0000000..6b41d0c Binary files /dev/null and b/calling-cpp/callee.o differ diff --git a/calling-cpp/callee.so b/calling-cpp/callee.so new file mode 100644 index 0000000..6711d24 Binary files /dev/null and b/calling-cpp/callee.so differ diff --git a/calling-cpp/caller.py b/calling-cpp/caller.py new file mode 100644 index 0000000..37d7c44 --- /dev/null +++ b/calling-cpp/caller.py @@ -0,0 +1,10 @@ +import ctypes +from ctypes import c_int64 + +my_lib = ctypes.CDLL('./callee.so') + +my_lib.sumprimes.argtypes = [ctypes.c_int64] +my_lib.sumprimes.restype = ctypes.c_int64 +x = input("input x:") +result = my_lib.sumprimes(int(x)) +print(result) \ No newline at end of file diff --git a/calling-cpp/workload.cpp b/calling-cpp/workload.cpp new file mode 100644 index 0000000..3f90eeb --- /dev/null +++ b/calling-cpp/workload.cpp @@ -0,0 +1,21 @@ +#include "workload.h" + +bool isprime(long long x){ + if (x < 2) return false; + for (long long i = 2; i*i <= x ; i++) { + if (x % i == 0) { + return false; + } + } + return true; +} + +long long sumprimes(long long bound){ + long long total = 0; + for (long long i = 1; i <= bound; i++){ + if (isprime(i)) { + total += i; + } + } + return total; +} \ No newline at end of file diff --git a/calling-cpp/workload.h b/calling-cpp/workload.h new file mode 100644 index 0000000..89d3db1 --- /dev/null +++ b/calling-cpp/workload.h @@ -0,0 +1,4 @@ +extern "C"{ + bool isprime(long long); + long long sumprimes(long long); +} \ No newline at end of file