前面我们用swig封装了c++的代码给java使用:
但是由于我们的代码写的太好了,python用户也想用,我们需要将c++代码封装一下给python用户使用。
这种需求很常见吧。
现在AI动不动就是用python进行训练,但是有些需要提高运行速度的时候还是需要用C/C++进行编写。
python与c++进行交互的方式好像挺多的。
ctype,pybind11,但这里我们还是使用swig。
代码还是使用我们在java那里的例子。
让我们看看我们优秀的代码是怎么给python程序员带来震撼的。
apple.h
#ifndef __APPLE_H__
#define __APPLE_H__
enum class LogLevel {
Trace /// Most detailed output
,Debug
,Info
,Warn
,Error
,Fatal /// Least detailed output
,Current /// no-op, value indicates current level should be retained
};
class Apple
{
public:
Apple();
int GetColor(void);
void SetColor(int color);
private:
int m_nColor;
};
#endif
apple.cpp
#include "apple.h"
Apple::Apple() : m_nColor(0)
{
}
void Apple::SetColor(int color)
{
m_nColor = color;
}
int Apple::GetColor(void)
{
return m_nColor;
}
我们在这两个文件里面定义了一个类Apple,给它添加了两个成员函数Setcolor和GetColor,是不是非常合理,一般的C++都是这么写的。
写swig接口文件 apple.i
%module demo
%{
/* Includes the header in the wrapper code */
#include "apple.h"
%}
/* Parse the header file to generate wrappers */
%include "apple.h"
用swig生成中间代码
swig -python -c++ apple.i
生成了 demo.py和 apple_wrap.cxx 两个文件
编译一下这个 cxx文件,生成 _demo.so
g++ -g -c apple.cpp -o apple.o
g++ -fpic -shared apple_wrap.cxx -o _demo.so -I/usr/include/python3.8 apple.o
用python调用一下试试。
main.py
import demo;
a = demo.Apple();
a.SetColor(2);
print(a.GetColor());
运行之
pp@dell:~/wrapper$ python3 main.py
2
非常好!
我们来看一下swig给我们生成的中间文件都有什么内容。
apple_wrap.cxx 的内容:
文件总共3804行,我们只看比较核心的部分,函数的实现。
SWIGINTERN PyObject *_wrap_Apple_SetColor(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
Apple *arg1 = (Apple *) 0 ;
int arg2 ;
void *argp1 = 0 ;
int res1 = 0 ;
int val2 ;
int ecode2 = 0 ;
PyObject *swig_obj[2] ;
if (!SWIG_Python_UnpackTuple(args, "Apple_SetColor", 2, 2, swig_obj)) SWIG_fail;
res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Apple, 0 | 0 );
if (!SWIG_IsOK(res1)) {
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Apple_SetColor" "', argument " "1"" of type '" "Apple *""'");
}
arg1 = reinterpret_cast< Apple * >(argp1);
ecode2 = SWIG_AsVal_int(swig_obj[1], &val2);
if (!SWIG_IsOK(ecode2)) {
SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "Apple_SetColor" "', argument " "2"" of type '" "int""'");
}
arg2 = static_cast< int >(val2);
(arg1)->SetColor(arg2);
resultobj = SWIG_Py_Void();
return resultobj;
fail:
return NULL;
}
SWIGINTERN PyObject *Apple_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *obj;
if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL;
SWIG_TypeNewClientData(SWIGTYPE_p_Apple, SWIG_NewClientData(obj));
return SWIG_Py_Void();
}
SWIGINTERN PyObject *Apple_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
return SWIG_Python_InitShadowInstance(args);
}
static PyMethodDef SwigMethods[] = {
{ "SWIG_PyInstanceMethod_New", SWIG_PyInstanceMethod_New, METH_O, NULL},
{ "new_Apple", _wrap_new_Apple, METH_NOARGS, NULL},
{ "Apple_GetColor", _wrap_Apple_GetColor, METH_O, NULL},
{ "Apple_SetColor", _wrap_Apple_SetColor, METH_VARARGS, NULL},
{ "delete_Apple", _wrap_delete_Apple, METH_O, NULL},
{ "Apple_swigregister", Apple_swigregister, METH_O, NULL},
{ "Apple_swiginit", Apple_swiginit, METH_VARARGS, NULL},
{ NULL, NULL, 0, NULL }
};
static PyMethodDef SwigMethods_proxydocs[] = {
{ NULL, NULL, 0, NULL }
};
demo.py的内容就比较简单了,只有90行
基本上 就是一个封装和函数调用转发。
# This file was automatically generated by SWIG (http://www.swig.org).
# Version 4.0.1
#
# Do not make changes to this file unless you know what you are doing--modify
# the SWIG interface file instead.
from sys import version_info as _swig_python_version_info
if _swig_python_version_info < (2, 7, 0):
raise RuntimeError("Python 2.7 or later required")
# Import the low-level C/C++ module
if __package__ or "." in __name__:
from . import _demo
else:
import _demo
try:
import builtins as __builtin__
except ImportError:
import __builtin__
def _swig_repr(self):
try:
strthis = "proxy of " + self.this.__repr__()
except __builtin__.Exception:
strthis = ""
return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
def _swig_setattr_nondynamic_instance_variable(set):
def set_instance_attr(self, name, value):
if name == "thisown":
self.this.own(value)
elif name == "this":
set(self, name, value)
elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
set(self, name, value)
else:
raise AttributeError("You cannot add instance attributes to %s" % self)
return set_instance_attr
def _swig_setattr_nondynamic_class_variable(set):
def set_class_attr(cls, name, value):
if hasattr(cls, name) and not isinstance(getattr(cls, name), property):
set(cls, name, value)
else:
raise AttributeError("You cannot add class attributes to %s" % cls)
return set_class_attr
def _swig_add_metaclass(metaclass):
"""Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass"""
def wrapper(cls):
return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())
return wrapper
class _SwigNonDynamicMeta(type):
"""Meta class to enforce nondynamic attributes (no new attributes) for a class"""
__setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)
LogLevel_Trace = _demo.LogLevel_Trace
LogLevel_Debug = _demo.LogLevel_Debug
LogLevel_Info = _demo.LogLevel_Info
LogLevel_Warn = _demo.LogLevel_Warn
LogLevel_Error = _demo.LogLevel_Error
LogLevel_Fatal = _demo.LogLevel_Fatal
LogLevel_Current = _demo.LogLevel_Current
class Apple(object):
thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
__repr__ = _swig_repr
def __init__(self):
_demo.Apple_swiginit(self, _demo.new_Apple())
def GetColor(self):
return _demo.Apple_GetColor(self)
def SetColor(self, color):
return _demo.Apple_SetColor(self, color)
__swig_destroy__ = _demo.delete_Apple
# Register Apple in _demo:
_demo.Apple_swigregister(Apple)
可以说,速度是非常的快。完全不用手动写多余的代码。
调用也非常简单,推荐使用SWIG。