本章涉及的主要内容:
- 链接模型
- Cython关键字 - cdef
- Typedef和函数指针
- public关键字
- 关键字cpdef
- C/C+++调用Python logging
- C/C++用Python ConfigParser
- Python到C/C++的回调
- Cython PXD
- 与构建系统集成
链接模型
C/C++中嵌入python
cython模式
cdef
cdef关键字告诉编译器该语句是本地C类型或本地函数。比如:
cdef int AddFunction(int, int)
def square(int x):
return x ** 2
Struct
C的struct 可以直接在Cython中使用。比如mycode.h
#ifndef __MYCODE_H__
#define __MYCODE_H__
struct mystruct {
char * string;
int integer;
char ** string_array;
};
extern void printStruct (struct mystruct *);
#endif //__MYCODE_H__
printStruct函数的实现 mycode.c
#include <stdio.h>
#include "mycode.h"
void printStruct (struct mystruct * s)
{
printf (".string = %s\n", s->string);
printf (".integer = %i\n", s->integer);
printf (".string_array = \n");
int i;
for (i = 0; i < s->integer; ++i)
printf ("\t[%i] = %s\n", i, s->string_array [i]);
}
Python导入调用:mycodepy.pyx
cdef extern from "mycode.h":
struct mystruct:
char * string
int integer
char ** string_array
void printStruct (mystruct *)
def testStruct ():
cdef mystruct s
cdef char *array [2]
s.string = "Hello World"
s.integer = 2
array [0] = "foo"
array [1] = "bar"
s.string_array = array
printStruct (&s)
嵌套结构体void myfunc (struct mystruct * x)在cython的定义:void myfunc (mystruct * x)。cython中没有→操作符号,要使用点号。
执行
$ make
cython -3 -o mycodepy.c mycodepy.pyx
gcc -g -O2 -fpic -c mycodepy.c -o mycodepy.o `python3-config --cflags`
gcc -g -O2 -fpic -c mycode.c -o mycode.o
gcc -g -O2 -shared -o mycodepy.so mycode.o mycodepy.o
$ python
Python 3.6.5 |Anaconda, Inc.| (default, Apr 29 2018, 16:14:56)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from mycodepy import testStruct
>>> from mycodepy import testStruct
>>> testStruct ()
.string = Hello World
.integer = 2
.string_array =
[0] = foo
[1] = bar
Enum
c中的实现:
enum cardsuit {
CLUBS,
DIAMONDS,
HEARTS,
SPADES
};
Cython定义
cdef enum cardsuit:
CLUBS, DIAMONDS, HEARTS, SPADES
# 使用
cdef cardsuit card = CLUBS
Typedef和函数指针
c中的实现:
struct foobar {
int x;
char * y;
};
typedef struct foobar foobar_t;
typedef void (*cfptr) (int)
Cython定义
cdef struct foobar:
int x
char * y
ctypedef foobar foobar_t
# 使用
ctypedef int * int_ptr
ctypedef void (*cfptr)(int
cdef cfptr myfunctionptr = &myfunc
Typedef和函数指针
这是Cython中非常强大的关键字。 它允许任何带有public修饰符的cdef声明输出相应的C/C++头,其中相关声明可从C/C++访问。 例如,我们可以声明:
cdef public struct CythonStruct:
size_t number_of_elements;
char ** elements;
编译后会生成cython_input.h
struct CythonStruct {
size_t number_of_elements;
char ** elements;
};
c调用要求在嵌入libpython.so,并执行初始化:
#include <Python.h>
int main(int argc, char **argv) {
Py_Initialize ();
// code in here
Py_Finalize ();
return 0;
}
调用前需要初始化cythonfile.pyx(如果有)的
cdef public void cythonFunction ():
print "inside cython function!!!"
这样会生成cythonfile.c和cythonfile.h。
/* Boiler plate init Python */
Py_SetProgramName (argv [0]);
Py_Initialize ();
/* Init our config module into Python memory */
initpublicTest ();
cythonFunction ();
/* cleanup python before exit... */
Py_Finalize ();
这个步骤类似python中的import cythonfile
cpdef
函数声明def在python和cython中可调用,cdef在c系列可以调用,cpdef在两者都可以调用,但是返回要知道类型,且丧失了cython的类型安全,不推荐这么做。
long returnValue = PyInt_AsLong (test (1, 0))
实例:C/C++调用python logging
main.c
#include "NativeLogging.h"
int main(int argc, char **argv)
{
// we want to ensure we use a command line argument for the output log file
if (argc < 2) {
return -1;
}
// use the first argument as log file
SetupNativeLogging(argv[1]);
// log out some stuff at different levels
info("info message");
debug("debug message");
error("error message");
// close up everything including Python
CloseNativeLogging();
return 0;
}
NativeLogging.h
#ifndef __NATIVE_LOGGING_H__
#define __NATIVE_LOGGING_H__
#define printflike __attribute__ ((format (printf, 3, 4)))
extern void printflike native_logging_info(const char *, unsigned, const char *, ...);
extern void printflike native_logging_debug(const char *, unsigned, const char *, ...);
extern void printflike native_logging_error(const char *, unsigned, const char *, ...);
#define info(...) native_logging_info(__FILE__, __LINE__, __VA_ARGS__)
#define error(...) native_logging_debug(__FILE__, __LINE__, __VA_ARGS__)
#define debug(...) native_logging_error(__FILE__, __LINE__, __VA_ARGS__)
extern void SetupNativeLogging(const char * logFileName);
extern void CloseNativeLogging();
#endif // __NATIVE_LOGGING_H__
NativeLogging.c
#include <Python.h>
#include <stdio.h>
#include <stdarg.h>
#include "PythonLoggingBackend.h"
#include "NativeLogging.h"
void native_logging_info(const char * file, unsigned line, const char * fmt, ...)
{
char buffer[256];
va_list args;
va_start(args, fmt);
vsprintf(buffer, fmt, args);
va_end(args);
char buf[512];
snprintf(buf, sizeof(buf), "%s:%i -> %s", file, line, buffer);
python_info(buf);
}
void native_logging_debug(const char * file, unsigned line,const char * fmt, ...)
{
char buffer[256];
va_list args;
va_start(args, fmt);
vsprintf(buffer, fmt, args);
va_end(args);
char buf[512];
snprintf(buf, sizeof(buf), "%s-%i -> %s", file, line, buffer);
python_debug(buf);
}
void native_logging_error(const char * file, unsigned line, const char * fmt, ...)
{
char buffer[256];
va_list args;
va_start(args, fmt);
vsprintf(buffer, fmt, args);
va_end(args);
char buf[512];
snprintf(buf, sizeof(buf), "%s:%i -> %s", file, line, buffer);
python_error(buf);
}
void SetupNativeLogging(const char * logFileName)
{
/* Boiler plate init Python */
Py_Initialize();
/* Init our config module into Python memory */
initPythonLoggingBackend();
/* call directly into our cython module parseConfig */
initLoggingWithLogFile(logFileName);
}
void CloseNativeLogging()
{
/* cleanup python before exit ... */
Py_Finalize();
}
PythonLoggingBackend.pyx
import logging
cdef public void initLoggingWithLogFile(const char * logfile):
logging.basicConfig(filename = logfile,
level = logging.DEBUG,
format = '%(levelname)s %(asctime)s: %(message)s',
datefmt = '%m/%d/%Y %I:%M:%S')
cdef public void python_info(char * message):
logging.info(message)
cdef public void python_debug(char * message):
logging.debug(message)
cdef public void python_error(char * message):
logging.error(message)
Makefile
all:
cython -2 PythonLoggingBackend.pyx
gcc -g -O2 -fpic -c PythonLoggingBackend.c -o PythonLoggingBackend.o `python-config --includes`
gcc -g -O2 -fpic -c NativeLogging.c -o NativeLogging.o `python-config --includes`
gcc -g -O2 -fpic -c main.c -o main.o
gcc -g -O2 -o example main.o PythonLoggingBackend.o NativeLogging.o `python-config --libs`
clean:
rm -f example PythonLoggingBackend.c *.o PythonLoggingBackend.h *.log