복붙노트

[PYTHON] C ++에서 호출 할 파이썬에서 C ++ 클래스를 구현하려면 어떻게해야합니까?

PYTHON

C ++에서 호출 할 파이썬에서 C ++ 클래스를 구현하려면 어떻게해야합니까?

C ++로 작성된 클래스 인터페이스가 있습니다. C ++로 작성된이 인터페이스를 구현하는 몇 가지 클래스가 있습니다. 이것들은 본질적으로 "main"을 구현하는보다 큰 C ++ 프로그램의 맥락에서 호출됩니다. 파이썬에서이 인터페이스의 구현체를 작성하고 C ++로 작성된 것처럼 더 큰 C ++ 프로그램의 컨텍스트에서 사용할 수 있기를 원합니다.

파이썬과 C ++의 인터페이싱에 관해 많이 쓰여졌지만, 내가 원하는 것을하는 방법을 알 수는 없습니다. 내가 찾을 수있는 가장 가까운 곳은 http://www.cs.brown.edu/~jwicks/boost/libs/python/doc/tutorial/doc/html/python/exposing.html#python.class_virtual_functions입니다. 그러나 이것은 isn이 아닙니다. 맞아.

좀 더 구체적으로, 기존 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
}

나는 이것이 충분히 명확하기를 바란다.)

해결법

  1. ==============================

    1.이 대답에는 두 부분이 있습니다. 먼저 파이썬에서 인터페이스를 공개해야합니다. 파이썬 구현은 자유롭게 인터페이스의 일부를 오버라이드 할 수 있습니다. 그렇다면 C ++ 프로그램을 보여줄 필요가있다.

    이 대답에는 두 부분이 있습니다. 먼저 파이썬에서 인터페이스를 공개해야합니다. 파이썬 구현은 자유롭게 인터페이스의 일부를 오버라이드 할 수 있습니다. 그렇다면 C ++ 프로그램을 보여줄 필요가있다.

    첫 번째 부분은 SWIG로하기 쉽습니다. 몇 가지 문제를 해결하기 위해 예제 시나리오를 약간 수정하고 테스트를 위해 추가 기능을 추가했습니다.

    // myif.h
    class myif {
       public:
         virtual float myfunc(float a) = 0;
    };
    
    inline void runCode(myif *inst) {
      std::cout << inst->myfunc(5) << std::endl;
    }
    

    지금은 응용 프로그램에 파이썬을 삽입하지 않고 문제를 살펴 보겠습니다. 즉, C ++의 int main ()이 아닌 Python에서 시작을 시작합니다. 나중에 그것을 추가하는 것은 상당히 간단합니다.

    우선 교차 언어 다형성이 작동합니다.

    %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
    

    그 시험에서 당신이보기를 바랄 것입니다.

    다음으로 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를 사용하여 .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;
    }
    

    마지막으로 main.cc를 -DSWIG_TYPE_TABLE = myif로 컴파일해야합니다.

    ./main
    11
    
  2. ==============================

    2.최소한의 예; Base는 순수 가상이 아니기 때문에 복잡합니다. 우리는 거기에 갈:

    최소한의 예; Base는 순수 가상이 아니기 때문에 복잡합니다. 우리는 거기에 갈:

    결과는 다음과 같습니다.

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

    여기서 fooBase () (가상이 아닌 C ++ 함수)가 virtual 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를 사용하여 파이썬 객체가 추출하려는 객체와 일치하는지 여부를 쿼리합니다 : PyObject * obj; py :: extract 추출기 (py :: object (obj)); if (! extractor.check ()) / * error * /; 자료 & b = 추출기 ();

  3. ==============================

    3.인용 http://wiki.python.org/moin/boost.python/ 상속

    인용 http://wiki.python.org/moin/boost.python/ 상속

    "Boost.Python은 또한 C ++ 상속 관계를 표현할 수있게하여 래핑 된 파생 클래스가 값, 포인터 또는 기본 클래스에 대한 참조가 인수로 전달 될 수 있도록합니다."

    가상 함수의 예가 있으므로 첫 번째 부분 (MyCl (myif) 클래스를 가진 부분)을 해결합니다.

    이를 수행하는 구체적인 예를 보려면 http://wiki.python.org/moin/boost.python/OverridableVirtualFunctions를 참조하십시오.

    myif 라인의 경우 c = MyCl (); 당신은 파이썬 (모듈)을 C ++에 노출시킬 필요가있다. 여기에 http://wiki.python.org/moin/boost.python/EmbeddingPython 예제가 있습니다.

  4. ==============================

    4.Eudoxos의 (매우 도움이되는) 대답을 바탕으로 나는 코드를 작성하고 내장 된 인터프리터가 내장 된 모듈을 확장했습니다.

    Eudoxos의 (매우 도움이되는) 대답을 바탕으로 나는 코드를 작성하고 내장 된 인터프리터가 내장 된 모듈을 확장했습니다.

    이 답변은 Boost.Python에 해당하는 SWIG 기반 대답입니다.

    헤더 파일 mpif.h :

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

    기본적으로 질문과 같지만 myfunc 및 가상 소멸자의 기본 구현이 있습니다.

    파이썬 구현을 위해 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을 사용하여 파이썬을 확장하는 방법은 무엇입니까?" (Eudoxos가 대답했습니다)는 "같은 프로그램에서 두 번 모두 어떻게해야합니까?"라는 질문에 대한 대답입니다. 이에 대한 해결책은 PyImport_AppendInittab 호출과 관련이 있습니다. PyImport_AppendInittab 호출은 모듈이로드 될 때 일반적으로 호출되어 모듈을 내장 모듈로 등록하는 초기화 함수를 사용합니다. 따라서 mycl.py가 import myif라고 말할 때, 내장 된 Boost.Python 모듈을 임포트하게된다.

  5. ==============================

    5.C ++과 Python을 연결하는 가장 다양하고 강력한 도구 인 Boost Python을 살펴보십시오.

    C ++과 Python을 연결하는 가장 다양하고 강력한 도구 인 Boost Python을 살펴보십시오.

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

  6. ==============================

    6.C ++ 코드를 Python과 직접 인터페이스하는 실제적인 방법은 없습니다.

    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에서 함수를 호출 할 수 있습니다.

    파이썬에서 :

    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 구조체를 다루지 않는다는 것입니다. 왜냐하면 SWIG가 이것을 처리하기 때문입니다.하지만 C에서 직접 감쌀 수는 있습니다. 여러분에게 달려 있습니다.

    다음은 ctypes에 대한 파이썬 문서입니다.

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

    또한 빌드 된 dll은 파이썬 인터페이스와 같은 폴더에 있어야합니다 (왜 그렇게되지 않을까요?).

    내가 궁금한데 왜 C ++을 직접 호출하지 않고 파이썬을 C ++ 내부에서 호출하고 싶습니까?

  7. from https://stackoverflow.com/questions/9040669/how-can-i-implement-a-c-class-in-python-to-be-called-by-c by cc-by-sa and MIT license