放弃 Julia 之后,我开始想要用 Python + ctypes 满足自己的需求。由于 Windows 搞这个很麻烦,就在 Ubuntu 上搞了,不过我是在 WSL 上做的哈哈。
Python 调用 C 示例
ctypes 官方中文文档中选择自己 Python 对应版本的文档。有很清晰的描述,然后在 Pythonlab.com 中很直观的例子:
1 2 3 4 5 6 7 8 9 10
| int add_int(int num1, int num2){ return num1 + num2; }
extern "C" int add_int(int num1,int num2); int add_int(int num1, int num2){ return num1 + num2; }
|
由于 C++ 支持函数重载功能,在编译时会更改函数名。
所以在函数声明时,前缀 extern "C"
则确保按 C 的方式编译。
然后把上面 add.c/add.cpp
编译成 .so
文件:
1 2 3
| $ gcc -fPIC -shared -o libadder.so add.c $ g++ -fPIC -shared -o libadder.so add.cpp
|
再调用就好了
1 2 3 4 5 6 7 8
| from ctypes import *
adder = CDLL('./libadder.so')
res_int = adder.add_int(4,5) print("Sum of 4 and 5 = ", res_int)
|
Python 调用自己写的 primepi C++ 函数
Python 调用自己写的 PrimePI 岂不美哉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| typedef long long LL; const int N = 1e7 + 2; int p[N], pi[N]; bool isp[N];
#include<math.h>
extern "C" { void init(); LL primepi(LL x); }
int initprime() { int cnt = 1; p[1] = 2; isp[2] = true; for (int i = 3; i < N; i += 2) isp[i] = true; for (int i = 3; i < N; i += 2) { if (isp[i]) p[++cnt] = i; for (int j = 2, t = (N - 1) / i + 1; j <= cnt && p[j] < t; ++j) { isp[i * p[j]] = false; if (i % p[j] == 0) break; } } return cnt; } const int M = 7; const int PM = 2 * 3 * 5 * 7 * 11 * 13 * 17; int phi[PM + 1][M + 1], sz[M + 1]; void init() { initprime(); pi[2] = 1; for (int i = 3; i < N; ++i) { if (isp[i]) pi[i] = pi[i - 1] + 1; else pi[i] = pi[i - 1]; } sz[0] = 1; for (int i = 0; i <= PM; ++i) phi[i][0] = i; for (int i = 1; i <= M; ++i) { sz[i] = p[i] * sz[i - 1]; for (int j = 1; j <= PM; ++j) { phi[j][i] = phi[j][i - 1] - phi[j / p[i]][i - 1]; } } } LL primepi(LL x); LL primephi(LL x, int s) { if (s <= M) return phi[x % sz[s]][s] + (x / sz[s]) * phi[sz[s]][s]; if (x / p[s] <= p[s]) return primepi(x) - s + 1; if (x < N && x / p[s] / p[s] <= p[s]) { int s2x = pi[(int)(sqrt(x + 0.2))]; LL ans = pi[x] - LL(s2x + s - 2) * (s2x - s + 1) / 2; for (int i = s + 1; i <= s2x; ++i) { ans += pi[x / p[i]]; } return ans; } return primephi(x, s - 1) - primephi(x / p[s], s - 1); } LL primepi(LL x) { if (x < N) return pi[x]; int ps2x = pi[int(sqrt(x + 0.2))]; int ps3x = pi[int(cbrt(x + 0.2))]; LL ans = primephi(x, ps3x) + LL(ps2x + ps3x - 2) * (ps2x - ps3x + 1) / 2; for (int i = ps3x + 1, ed = ps2x; i <= ed; ++i) { ans -= primepi(x / p[i]); } return ans; }
|
执行 g++ -fPIC -shared -o libprimepi.so primepi.cpp
后运行下面 Python 程序
1 2 3 4 5 6
| from ctypes import * c = CDLL('./libprimepi.so') c.init()
print(c.primepi(9876543210))
|
C 中用 Python 老是提醒没有 Python.h
,试了网上的方法,各种系统都不行服了。不过无所谓最在 C 中用 Python 啊
Make
既然要混合编程了,哪必然要涉及到 makefile 了,于是我去 知乎
示例源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int main() { printf("hello world\n"); fun1(); fun2(); }
void fun1() { printf("this is fun1\n"); }
void fun2() { printf("this is fun2\n"); }
|
示例 Makefile(注意 Makefile 一定要 Tab 缩进)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| app: main.c fun1.c fun2.c gcc main.c fun1.c fun2.c -o app
app: main.o fun1.o fun2.o gcc main.o fun1.o fun2.o -o app main.o: main.c gcc -c main.c -o main.o fun1.o: fun1.c gcc -c fun1.c -o fun1.o fun2.o: fun2.c gcc -c fun2.c -o fun2.o
obj = main.o fun1.o fun2.o target = app CC = gcc $(target): $(obj) $(CC) $(obj) -o $(target) %.o: %.c $(CC) -c $< -o $@
src = $(wildcard ./*.c) obj = $(patsubst %.c, %.o, $(src)) target = app CC = gcc $(target): $(obj) $(CC) $(obj) -o $(target) %.o: %.c $(CC) -c $< -o $@ .PHONY: clean clean: rm -rf $(obj) $(target)
|
其中 $
自然是取值操作 %
是未定元的感觉,然后 wildcard, patsubst
从版本中就能看出。
$<
:第一个依赖文件;$@
:目标文件;$^
:所有不重复的依赖文件,以空格分开。
依次执行 make -f makefilei
即可,最后 make (-f makefile 可省略), make clean
是最终版本
这里写的很详细