我怎样才能实现一个C++ Python 中的类,由 C++ 调用?

发布于 2024-12-29 04:59:38 字数 1007 浏览 0 评论 0 原文

我有一个用 C++ 编写的类接口。我有一些实现这个接口的类也是用 C++ 编写的。这些在较大的 C++ 程序的上下文中调用,该程序本质上实现了“main”。我希望能够在 Python 中编写此接口的实现,并允许它们在更大的 C++ 程序的上下文中使用,就像它们刚刚用 C++ 编写一样。

关于 python 和 C++ 的接口已经有很多文章了,但我不太清楚如何做我想做的事情。我能找到的最接近的是这里:http://www.cs .brown.edu/~jwicks/boost/libs/python/doc/tutorial/doc/html/python/exusing.html#python.class_virtual_functions,但这不太正确。

更具体地说,假设我有一个现有的 C++ 接口,定义如下:

// myif.h
class myif {
   public:
     virtual float myfunc(float a);
};

我想要做的事情是:

// mycl.py
... some magic python stuff ...
class MyCl(myif):
  def myfunc(a):
    return a*2

然后,回到我的 C++ 代码中,我希望能够这样说:

// mymain.cc
void main(...) {
  ... some magic c++ stuff ...
  myif c = MyCl();  // get the python class
  cout << c.myfunc(5) << endl;  // should print 10
}

我希望这是足够清楚;)

I have a class interface written in C++. I have a few classes that implement this interface also written in C++. These are called in the context of a larger C++ program, which essentially implements "main". I want to be able to write implementations of this interface in Python, and allow them to be used in the context of the larger C++ program, as if they had been just written in C++.

There's been a lot written about interfacing python and C++ but I cannot quite figure out how to do what I want. The closest I can find is here: http://www.cs.brown.edu/~jwicks/boost/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions, but this isn't quite right.

To be more concrete, suppose I have an existing C++ interface defined something like:

// myif.h
class myif {
   public:
     virtual float myfunc(float a);
};

What I want to be able to do is something like:

// mycl.py
... some magic python stuff ...
class MyCl(myif):
  def myfunc(a):
    return a*2

Then, back in my C++ code, I want to be able to say something like:

// mymain.cc
void main(...) {
  ... some magic c++ stuff ...
  myif c = MyCl();  // get the python class
  cout << c.myfunc(5) << endl;  // should print 10
}

I hope this is sufficiently clear ;)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

月隐月明月朦胧 2025-01-05 04:59:38

这个答案有两个部分。首先,您需要以允许 Python 实现随意覆盖部分接口的方式在 Python 中公开接口。然后你需要展示你的 C++ 程序(在 main 中如何调用 Python)。


向 Python 公开现有接口:

第一部分使用 SWIG 很容易完成。我稍微修改了你的示例场景来修复一些问题并添加了一个额外的测试函数:

// myif.h
class myif {
   public:
     virtual float myfunc(float a) = 0;
};

inline void runCode(myif *inst) {
  std::cout << inst->myfunc(5) << std::endl;
}

现在我将在不将 Python 嵌入到您的应用程序中的情况下查看问题,即您在 Python 中启动 excetion,而不是在 C++ 中的 int main() 中。稍后添加相当简单不过,

首先要跨语言多态性工作

%module(directors="1") module

// We need to include myif.h in the SWIG generated C++ file
%{
#include <iostream>
#include "myif.h"
%}

// Enable cross-language polymorphism in the SWIG wrapper. 
// It's pretty slow so not enable by default
%feature("director") myif;

// Tell swig to wrap everything in myif.h
%include "myif.h"

为此,我们启用了 SWIG不过,其余部分是非常标准的 SWIG

实现:

import module

class MyCl(module.myif):
  def __init__(self):
    module.myif.__init__(self)
  def myfunc(self,a):
    return a*2.0

cl = MyCl()

print cl.myfunc(100.0)

module.runCode(cl)

然后我就可以编译并运行它:

swig -python  -c++ -Wall myif.i 
g++ -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7

python mycl.py 
200.0
10

正是您希望从该测试中看到的内容。


嵌入Python在应用程序中:

接下来我们需要实现 mymain.cc 的真实版本,我已经整理了它的外观草图:

#include <iostream>
#include "myif.h"
#include <Python.h>

int main()
{
  Py_Initialize();

  const double input = 5.0;

  PyObject *main = PyImport_AddModule("__main__");
  PyObject *dict = PyModule_GetDict(main);
  PySys_SetPath(".");
  PyObject *module = PyImport_Import(PyString_FromString("mycl"));
  PyModule_AddObject(main, "mycl", module);

  PyObject *instance = PyRun_String("mycl.MyCl()", Py_eval_input, dict, dict);
  PyObject *result = PyObject_CallMethod(instance, "myfunc", (char *)"(O)" ,PyFloat_FromDouble(input));

  PyObject *error = PyErr_Occurred();
  if (error) {
    std::cerr << "Error occured in PyRun_String" << std::endl;
    PyErr_Print();
  }

  double ret = PyFloat_AsDouble(result);
  std::cout << ret << std::endl;

  Py_Finalize();
  return 0;
}

它基本上只是标准的 将 Python 嵌入到另一个应用程序。它的工作原理并给出了您希望看到的内容:

g++ -Wall -Wextra -I/usr/include/python2.7 main.cc -o main -lpython2.7
./main
200.0
10
10

难题的最后一部分是能够将您在 Python 中创建实例时获得的 PyObject* 转换为 myif *。 SWIG 再次使这变得相当简单。

首先,我们需要要求 SWIG 在头文件中为我们公开其运行时。我们通过额外调用 SWIG 来完成此操作:

swig -Wall -c++ -python -external-runtime runtime.h

接下来,我们需要重新编译 SWIG 模块,显式给出 SWIG 知道的类型表的名称,以便我们可以从 main.cc 中查找它。我们使用以下方法重新编译 .so:

g++ -DSWIG_TYPE_TABLE=myif -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7

然后,我们在 main.cc 中添加一个辅助函数,用于将 PyObject* 转换为 myif*

#include "runtime.h"
// runtime.h was generated by SWIG for us with the second call we made

myif *python2interface(PyObject *obj) {
  void *argp1 = 0;
  swig_type_info * pTypeInfo = SWIG_TypeQuery("myif *");

  const int res = SWIG_ConvertPtr(obj, &argp1,pTypeInfo, 0);
  if (!SWIG_IsOK(res)) {
    abort();
  }
  return reinterpret_cast<myif*>(argp1);
}

现在,我们可以使用它了从 main() 内部:

int main()
{
  Py_Initialize();

  const double input = 5.5;

  PySys_SetPath(".");
  PyObject *module = PyImport_ImportModule("mycl");

  PyObject *cls = PyObject_GetAttrString(module, "MyCl");
  PyObject *instance = PyObject_CallFunctionObjArgs(cls, NULL);

  myif *inst = python2interface(instance);
  std::cout << inst->myfunc(input) << std::endl;

  Py_XDECREF(instance);
  Py_XDECREF(cls);

  Py_Finalize();
  return 0;
}

最后我们必须使用 -DSWIG_TYPE_TABLE=myif 编译 main.cc,这给出:

./main
11

There's two parts to this answer. First you need to expose your interface in Python in a way which allows Python implementations to override parts of it at will. Then you need to show your C++ program (in main how to call Python.


Exposing the existing interface to Python:

The first part is pretty easy to do with SWIG. I modified your example scenario slightly to fix a few issues and added an extra function for testing:

// myif.h
class myif {
   public:
     virtual float myfunc(float a) = 0;
};

inline void runCode(myif *inst) {
  std::cout << inst->myfunc(5) << std::endl;
}

For now I'll look at the problem without embedding Python in your application, i.e. you start excetion in Python, not in int main() in C++. It's fairly straightforward to add that later though.

First up is getting cross-language polymorphism working:

%module(directors="1") module

// We need to include myif.h in the SWIG generated C++ file
%{
#include <iostream>
#include "myif.h"
%}

// Enable cross-language polymorphism in the SWIG wrapper. 
// It's pretty slow so not enable by default
%feature("director") myif;

// Tell swig to wrap everything in myif.h
%include "myif.h"

To do that we've enabled SWIG's director feature globally and specifically for our interface. The rest of it is pretty standard SWIG though.

I wrote a test Python implementation:

import module

class MyCl(module.myif):
  def __init__(self):
    module.myif.__init__(self)
  def myfunc(self,a):
    return a*2.0

cl = MyCl()

print cl.myfunc(100.0)

module.runCode(cl)

With that I was then able to compile and run this:

swig -python  -c++ -Wall myif.i 
g++ -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7

python mycl.py 
200.0
10

Exactly what you'd hope to see from that test.


Embedding the Python in the application:

Next up we need to implement a real version of your mymain.cc. I've put together a sketch of what it might look like:

#include <iostream>
#include "myif.h"
#include <Python.h>

int main()
{
  Py_Initialize();

  const double input = 5.0;

  PyObject *main = PyImport_AddModule("__main__");
  PyObject *dict = PyModule_GetDict(main);
  PySys_SetPath(".");
  PyObject *module = PyImport_Import(PyString_FromString("mycl"));
  PyModule_AddObject(main, "mycl", module);

  PyObject *instance = PyRun_String("mycl.MyCl()", Py_eval_input, dict, dict);
  PyObject *result = PyObject_CallMethod(instance, "myfunc", (char *)"(O)" ,PyFloat_FromDouble(input));

  PyObject *error = PyErr_Occurred();
  if (error) {
    std::cerr << "Error occured in PyRun_String" << std::endl;
    PyErr_Print();
  }

  double ret = PyFloat_AsDouble(result);
  std::cout << ret << std::endl;

  Py_Finalize();
  return 0;
}

It's basically just standard embedding Python in another application. It works and gives exactly what you'd hope to see also:

g++ -Wall -Wextra -I/usr/include/python2.7 main.cc -o main -lpython2.7
./main
200.0
10
10

The final piece of the puzzle is being able to convert the PyObject* that you get from creating the instance in Python into a myif *. SWIG again makes this reasonably straightforward.

First we need to ask SWIG to expose its runtime in a headerfile for us. We do this with an extra call to SWIG:

swig -Wall -c++ -python -external-runtime runtime.h

Next we need to re-compile our SWIG module, explicitly giving the table of types SWIG knows about a name so we can look it up from within our main.cc. We recompile the .so using:

g++ -DSWIG_TYPE_TABLE=myif -Wall -Wextra -shared -o _module.so myif_wrap.cxx -I/usr/include/python2.7 -lpython2.7

Then we add a helper function for converting the PyObject* to myif* in our main.cc:

#include "runtime.h"
// runtime.h was generated by SWIG for us with the second call we made

myif *python2interface(PyObject *obj) {
  void *argp1 = 0;
  swig_type_info * pTypeInfo = SWIG_TypeQuery("myif *");

  const int res = SWIG_ConvertPtr(obj, &argp1,pTypeInfo, 0);
  if (!SWIG_IsOK(res)) {
    abort();
  }
  return reinterpret_cast<myif*>(argp1);
}

Now this is in place we can use it from within main():

int main()
{
  Py_Initialize();

  const double input = 5.5;

  PySys_SetPath(".");
  PyObject *module = PyImport_ImportModule("mycl");

  PyObject *cls = PyObject_GetAttrString(module, "MyCl");
  PyObject *instance = PyObject_CallFunctionObjArgs(cls, NULL);

  myif *inst = python2interface(instance);
  std::cout << inst->myfunc(input) << std::endl;

  Py_XDECREF(instance);
  Py_XDECREF(cls);

  Py_Finalize();
  return 0;
}

Finally we have to compile main.cc with -DSWIG_TYPE_TABLE=myif and this gives:

./main
11
耀眼的星火 2025-01-05 04:59:38

最小的例子;请注意,Base 不是纯虚拟的,这一事实使情况变得复杂。我们开始:

  1. baz.cpp:

    <前><代码>#include<字符串>
    #include
    使用 std::string ;
    命名空间 py=boost::python;

    结构基数{
    虚拟字符串 foo() const { return "Base.foo"; }
    // fooBase 是非虚拟的,可以从任何地方调用它(c++ 或 python)
    // 将通过c++调度
    字符串 fooBase() const { 返回 foo(); }
    };
    struct BaseWrapper: Base, py::wrapper{
    字符串 foo() 常量{
    // 如果 Base 是抽象的(在 python 中不可实例化),那么
    // 这里只有 this->get_override("foo")()
    //
    // 如果在 python 中调用覆盖 foo 的类
    if(this->get_override("foo")) 返回 this->get_override("foo")();
    // python 中没有覆盖;如果直接实例化 Base(Wrapper) 就会发生
    否则返回 Base::foo();
    }
    };

    BOOST_PYTHON_MODULE(巴兹){
    py::class_("Base")
    .def("foo",&Base::foo)
    .def("fooBase",&Base::fooBase)
    ;
    }

  2. bar.py

    导入系统
    sys.path.append('.')
    导入巴兹
    
    类 PyDerived(baz.Base):
      def foo(self): 返回 'PyDerived.foo'
    
    基=baz.Base()
    der=PyDerived()
    打印base.foo(),base.fooBase()
    打印 der.foo(), der.fooBase()
    
  3. Makefile

    <前><代码>默认:
    g++ -shared -fPIC -o baz.so baz.cpp -lboost_python `pkg-config python --cflags`

结果是:

Base.foo Base.foo
PyDerived.foo PyDerived.foo

您可以在其中看到 fooBase() (非虚拟 C++ 函数)如何调用虚拟 foo(),无论是在 C++ 还是 Python 中,都会解析为覆盖。你可以在 C++ 中从 Base 派生一个类,它的工作原理是一样的。

编辑(提取c++对象):

PyObject* obj; // given
py::object pyObj(obj); // wrap as boost::python object (cheap)
py::extract<Base> ex(pyObj); 
if(ex.check()){ // types are compatible
  Base& b=ex(); // get the wrapped object
  // ...
} else {
  // error
}

// shorter, thrwos when conversion not possible
Base &b=py::extract<Base>(py::object(obj))();

PyObject*构造py::object并使用py::extract查询 python 对象是否与您尝试提取的内容匹配:PyObject* obj; py::extract<基础>提取器(py::对象(obj)); if(!extractor.check()) /* 错误 */;基地& b=提取器();

Minimal example; note that it is complicated by the fact that Base is not pure virtual. There we go:

  1. baz.cpp:

    #include<string>
    #include<boost/python.hpp>
    using std::string;
    namespace py=boost::python;
    
    struct Base{
      virtual string foo() const { return "Base.foo"; }
      // fooBase is non-virtual, calling it from anywhere (c++ or python)
      // will go through c++ dispatch
      string fooBase() const { return foo(); }
    };
    struct BaseWrapper: Base, py::wrapper<Base>{
      string foo() const{
        // if Base were abstract (non-instantiable in python), then
        // there would be only this->get_override("foo")() here
        //
        // if called on a class which overrides foo in python
        if(this->get_override("foo")) return this->get_override("foo")();
        // no override in python; happens if Base(Wrapper) is instantiated directly
        else return Base::foo();
      }
    };
    
    BOOST_PYTHON_MODULE(baz){
      py::class_<BaseWrapper,boost::noncopyable>("Base")
        .def("foo",&Base::foo)
        .def("fooBase",&Base::fooBase)
      ;
    }
    
  2. bar.py

    import sys
    sys.path.append('.')
    import baz
    
    class PyDerived(baz.Base):
      def foo(self): return 'PyDerived.foo'
    
    base=baz.Base()
    der=PyDerived()
    print base.foo(), base.fooBase()
    print der.foo(), der.fooBase()
    
  3. Makefile

    default:
           g++ -shared -fPIC -o baz.so baz.cpp -lboost_python `pkg-config python --cflags`
    

And the result is:

Base.foo Base.foo
PyDerived.foo PyDerived.foo

where you can see how fooBase() (the non-virtual c++ function) calls virtual foo(), which resolves to the override regardless whether in c++ or python. You could derive a class from Base in c++ and it would work just the same.

EDIT (extracting c++ object):

PyObject* obj; // given
py::object pyObj(obj); // wrap as boost::python object (cheap)
py::extract<Base> ex(pyObj); 
if(ex.check()){ // types are compatible
  Base& b=ex(); // get the wrapped object
  // ...
} else {
  // error
}

// shorter, thrwos when conversion not possible
Base &b=py::extract<Base>(py::object(obj))();

Construct py::object from PyObject* and use py::extract to query whether the python object matches what you are trying to extract: PyObject* obj; py::extract<Base> extractor(py::object(obj)); if(!extractor.check()) /* error */; Base& b=extractor();

万劫不复 2025-01-05 04:59:38

引用 http://wiki.python.org/moin/boost.python/Inheritance

“Boost.Python 还允许我们表示 C++ 继承关系,以便可以传递包装的派生类,其中值、指针或对基类的引用需要作为参数。”

有一些虚函数的示例,可以解决第一部分(带有类 MyCl(myif) 的部分)

对于执行此操作的具体示例,http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions

对于 myif c = MyCl(); 行你需要将你的Python(模块)暴露给C++。这里有一些例子 http://wiki.python.org/moin/boost.python/EmbeddingPython

Quoting http://wiki.python.org/moin/boost.python/Inheritance

"Boost.Python also allows us to represent C++ inheritance relationships so that wrapped derived classes may be passed where values, pointers, or references to a base class are expected as arguments."

There are examples of virtual functions so that solves the first part (the one with class MyCl(myif))

For specific examples doing this, http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions

For the line myif c = MyCl(); you need to expose your python (module) to C++. There are examples here http://wiki.python.org/moin/boost.python/EmbeddingPython

唯憾梦倾城 2025-01-05 04:59:38

基于Eudoxos的(非常有帮助的)答案我已经采用了他的代码并扩展了它,现在有一个嵌入式解释器,带有内置模块。

这个答案在 Boost.Python 中相当于我基于 SWIG 的答案

头文件 myif.h:

class myif {
public:
  virtual float myfunc(float a) const { return 0; }
  virtual ~myif() {}
};

基本上与问题中一样,但具有 myfunc 的默认实现和虚拟析构函数。

对于Python实现,MyCl.py我基本上与问题相同:

import myif

class MyCl(myif.myif):
  def myfunc(self,a): 
    return a*2.0

然后剩下mymain.cc,其中大部分基于Eudoxos的答案:

#include <boost/python.hpp>
#include <iostream>
#include "myif.h"

using namespace boost::python;

// This is basically Eudoxos's answer:
struct MyIfWrapper: myif, wrapper<myif>{
  float myfunc(float a) const {
    if(this->get_override("myfunc")) 
      return this->get_override("myfunc")(a);
    else 
      return myif::myfunc(a);
  }
};

BOOST_PYTHON_MODULE(myif){
  class_<MyIfWrapper,boost::noncopyable>("myif")
    .def("myfunc",&myif::myfunc)
  ;
}
// End answer by Eudoxos

int main( int argc, char ** argv ) {
  try {
    // Tell python that "myif" is a built-in module
    PyImport_AppendInittab("myif", initmyif);
    // Set up embedded Python interpreter:
    Py_Initialize();

    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");

    PySys_SetPath(".");
    main_namespace["mycl"] = import("mycl");

    // Create the Python object with an eval()
    object obj = eval("mycl.MyCl()", main_namespace);

    // Find the base C++ type for the Python object (from Eudoxos)
    const myif &b=extract<myif>(obj)();
    std::cout << b.myfunc(5) << std::endl;

  } catch( error_already_set ) {
    PyErr_Print();
  }
}

我在这里添加的关键部分,高于“如何使用 Boost.Python 嵌入 Python?”以及“如何使用 Boost.python 扩展 Python?” (由 Eudoxos 回答)是“我如何在同一个程序中同时执行这两个操作?”这个问题的答案。解决这个问题的方法在于 PyImport_AppendInittab call,它采用通常在加载模块时调用的初始化函数并将其注册为内置模块。因此,当 mycl.py 说 import myif 时,它最终会导入内置的 Boost.Python 模块。

Based upon the (very helpful) answer by Eudoxos I've taken his code and extended it such that there is now an embedded interpreter, with a built-in module.

This answer is the Boost.Python equivalent of my SWIG based answer.

The headerfile myif.h:

class myif {
public:
  virtual float myfunc(float a) const { return 0; }
  virtual ~myif() {}
};

Is basically as in the question, but with a default implementation of myfunc and a virtual destructor.

For the Python implementation, MyCl.py I have basically the same as the question:

import myif

class MyCl(myif.myif):
  def myfunc(self,a): 
    return a*2.0

This then leaves mymain.cc, most of which is based upon the answer from Eudoxos:

#include <boost/python.hpp>
#include <iostream>
#include "myif.h"

using namespace boost::python;

// This is basically Eudoxos's answer:
struct MyIfWrapper: myif, wrapper<myif>{
  float myfunc(float a) const {
    if(this->get_override("myfunc")) 
      return this->get_override("myfunc")(a);
    else 
      return myif::myfunc(a);
  }
};

BOOST_PYTHON_MODULE(myif){
  class_<MyIfWrapper,boost::noncopyable>("myif")
    .def("myfunc",&myif::myfunc)
  ;
}
// End answer by Eudoxos

int main( int argc, char ** argv ) {
  try {
    // Tell python that "myif" is a built-in module
    PyImport_AppendInittab("myif", initmyif);
    // Set up embedded Python interpreter:
    Py_Initialize();

    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");

    PySys_SetPath(".");
    main_namespace["mycl"] = import("mycl");

    // Create the Python object with an eval()
    object obj = eval("mycl.MyCl()", main_namespace);

    // Find the base C++ type for the Python object (from Eudoxos)
    const myif &b=extract<myif>(obj)();
    std::cout << b.myfunc(5) << std::endl;

  } catch( error_already_set ) {
    PyErr_Print();
  }
}

The key part that I've added here, above and beyond the "how do I embed Python using Boost.Python?" and "how do I extend Python using Boost.python?" (which was answered by Eudoxos) is the answer to the question "How do I do both at once in the same program?". The solution to this lies with the PyImport_AppendInittab call, which takes the initialisation function that would normally be called when the module is loaded and registers it as a built-in module. Thus when mycl.py says import myif it ends up importing the built-in Boost.Python module.

最好是你 2025-01-05 04:59:38

看一下 Boost Python,它是连接 C++ 和 Python 的最通用、最强大的工具。

http://www.boost.org/doc/libs/1_48_0/库/python/doc/

Take a look at Boost Python, that is the most versatile and powerful tool to bridge between C++ and Python.

http://www.boost.org/doc/libs/1_48_0/libs/python/doc/

佞臣 2025-01-05 04:59:38

没有真正的方法可以将 C++ 代码直接与 Python 连接。

SWIG 确实可以处理这个问题,但它构建了自己的包装器。

相对于 SWIG,我更喜欢的一种替代方案是 ctypes,但要使用它,您需要创建一个 C 包装器。

例如:

// myif.h
class myif {
   public:
     virtual float myfunc(float a);
};

像这样构建一个 C 包装器:

extern "C" __declspec(dllexport) float myif_myfunc(myif* m, float a) {
    return m->myfunc(a);
}

由于您使用 C++ 进行构建,因此 extern“C” 允许 C 链接,因此您可以从 dll 轻松调用它,并且 __declspec(dllexport) 允许从dll。

在 Python 中:

from ctypes import *
from os.path import dirname

dlldir = dirname(__file__)                      # this strips it to the directory only
dlldir.replace( '\\', '\\\\' )                  # Replaces \ with \\ in dlldir
lib = cdll.LoadLibrary(dlldir+'\\myif.dll')     # Loads from the full path to your module.

# Just an alias for the void pointer for your class
c_myif = c_void_p

# This tells Python how to interpret the return type and arguments
lib.myif_myfunc.argtypes = [ c_myif, c_float ]
lib.myif_myfunc.restype  = c_float

class MyCl(myif):
    def __init__:
        # Assume you wrapped a constructor for myif in C
        self.obj = lib.myif_newmyif(None)

    def myfunc(a):
        return lib.myif_myfunc(self.obj, a)

虽然 SWIG 为您完成了所有这些工作,但您几乎没有空间可以随意修改内容,而不会因重新生成 SWIG 包装器时必须重做的所有更改而感到沮丧。

ctypes 的一个问题是它不处理 STL 结构,因为它是为 C 设计的。SWIG 确实为您处理了这个问题,但您也许可以自己将其包装在 C 中。这取决于您。

这是 ctypes 的 Python 文档:

http://docs.python.org/library/ctypes.html< /a>

此外,构建的 dll 应该与 Python 接口位于同一文件夹中(为什么不呢?)。

不过我很好奇,为什么要从 C++ 内部调用 Python,而不是直接调用 C++ 实现?

There's no real way to interface C++ code directly with Python.

SWIG does handle this, but it builds its own wrapper.

One alternative I prefer over SWIG is ctypes, but to use this you need to create a C wrapper.

For the example:

// myif.h
class myif {
   public:
     virtual float myfunc(float a);
};

Build a C wrapper like so:

extern "C" __declspec(dllexport) float myif_myfunc(myif* m, float a) {
    return m->myfunc(a);
}

Since you are building using C++, the extern "C" allows for C linkage so you can call it easily from your dll, and __declspec(dllexport) allows the function to be called from the dll.

In Python:

from ctypes import *
from os.path import dirname

dlldir = dirname(__file__)                      # this strips it to the directory only
dlldir.replace( '\\', '\\\\' )                  # Replaces \ with \\ in dlldir
lib = cdll.LoadLibrary(dlldir+'\\myif.dll')     # Loads from the full path to your module.

# Just an alias for the void pointer for your class
c_myif = c_void_p

# This tells Python how to interpret the return type and arguments
lib.myif_myfunc.argtypes = [ c_myif, c_float ]
lib.myif_myfunc.restype  = c_float

class MyCl(myif):
    def __init__:
        # Assume you wrapped a constructor for myif in C
        self.obj = lib.myif_newmyif(None)

    def myfunc(a):
        return lib.myif_myfunc(self.obj, a)

While SWIG does all this for you, there's little room for you to modify things as you please without getting frustrated at all the changes you have to redo when you regenerate the SWIG wrapper.

One issue with ctypes is that it doesn't handle STL structures, since it's made for C. SWIG does handle this for you, but you may be able to wrap it yourself in the C. It's up to you.

Here's the Python doc for ctypes:

http://docs.python.org/library/ctypes.html

Also, the built dll should be in the same folder as your Python interface (why wouldn't it be?).

I am curious though, why would you want to call Python from inside C++ instead of calling the C++ implementation directly?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文