- PVSM.RU - https://www.pvsm.ru -

Интеграция Python и C++

Всем доброго времени суток!

Недавно при прототипировании одной из частей разрабатываемого нами продукта возникла одна интересная задача: нужно было проверить склейку Python и C++. Связано это было с тем, что основной код был написан на плюсах, и необходимо было подключить внешнюю библиотеку Websockets, написанную на Python (на тот момент не было соответствующей библиотеки на C++). Схема взаимодействия при такой задаче достаточно простая. Из C++ вызывается функция подключения к серверу (на python), в качестве параметра передается его адрес. Соответственно, при получении сообщния Python передавает его обратно в метод C++.

При написании кода использовалась питоновская библиотека Websocket от Autobahn (http://www.tavendo.de/autobahn/clientlibraries.html [1]), которую было необходимо вызывать из C++. Для этих целей в Python предусмотрен Python C-API (http://docs.python.org/extending/index.html [2]), однако многие простые действия, например, вызов функций делается несколькими действиями. После небольшого гугления был найден ряд библиотек, позволяющих упростить подобные действия: Boost.Python (http://www.boost.org/doc/libs/1_39_0/libs/python/doc/index.html [3]), SWIG(http://www.swig.org/ [4]), Py++, Pybindgen, Pyrex… В результате был выбран Boost.Python как наиболее популярное решение.

Для начала напишем простенький эхо клиент на Python, который будет раз в секунду посылать сам себе сообщение “Hello world”, принимать его и отдавать в C++. cppMethods будет объявлен в C++ коде, cppMethods.printMessage(msg) — как раз место склейки со стороны Python, непосредственно вызов функции C++, которая будет печатать полученное сообщение.

Вот код Python — echo-client.py:

from twisted.internet import reactor
from autobahn.websocket import WebSocketClientFactory, WebSocketClientProtocol, connectWS
  
import cppMethods 
  
class EchoClientProtocol(WebSocketClientProtocol):
  
   def sendHello(self):
      self.sendMessage("Hello, world!")  
  
   def onOpen(self):
      self.sendHello()
  
   def onMessage(self, msg, binary):
      cppMethods.printMessage(msg)
      reactor.callLater(1, self.sendHello)
  
def Connect(addressStr):
    factory = WebSocketClientFactory(addressStr)
    factory.protocol = EchoClientProtocol
    connectWS(factory)
    reactor.run()

Теперь напишем на C++ код, в котором опишем нашу функцию, вызываемую из питона. Для использования Python C-API нужно проинклудить Python.h. Обратите внимание, что на этом этапе мы еще не используем Boost.Python, лишь собственно родной Python C-API.

PrintEmb.cpp:

#include <Python.h>

#include <iostream>
#include <string>
 
static PyObject * 
printString(PyObject * self, PyObject* args)
{
    std::cout << PyString_AsString(args) << std::endl;
    return Py_BuildValue("");
}
  
static PyMethodDef EmbMethods[] = {
    {"printMessage", printString},
    {NULL, NULL}
};

В последнем объявлении мы описали, что при вызове функции printMessage из Python будет вызван C++ метод printString.

Ну и наконец свяжем все это вместе. Для проверки работы websockets, помимо эхо-клиента, была использована ссылка на html5labs.

WebSocketConnect.cpp:

#include <Python.h>
#include <boost/python.hpp>
  
#include <iostream>
#include <string>
#include "PrintEmb.cpp" 
  
void WebSocketConnect()
{
    using namespace boost::python;
     
    Py_Initialize();
     
    Py_InitModule("cppMethods", EmbMethods);
     
    PyObject * ws = PyImport_ImportModule("echo_client");
    std::string address = "ws://html5labs-interop.cloudapp.net:4502/chat";
    call_method<void>(ws, "Connect", address);
     
    Py_Finalize();
}

В этом месте мы все-таки воспользовались возможностями Boost, а именно функцией call_method, иначе бы нам понадобилось написать существенно больше кода.

Ну вот как-то так. Здесь мы поинициализировали EmbMethods для питона, назвав их cppMethods, а затем вызвали из Python метод Connect и передали в него строку “address”. В результате наше приложение раз в секунду печатает строку «Hello World» (которую посылает сам себе питон), а также любое сообщение, приходящее с сервера вебсокетов.

Вот таким образом можно связать Python и C++. Буду благодарен за комментарии по теме.

Автор: rigeborod


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/3834

Ссылки в тексте:

[1] http://www.tavendo.de/autobahn/clientlibraries.html: http://www.tavendo.de/autobahn/clientlibraries.html

[2] http://docs.python.org/extending/index.html: http://docs.python.org/extending/index.html

[3] http://www.boost.org/doc/libs/1_39_0/libs/python/doc/index.html: http://www.boost.org/doc/libs/1_39_0/libs/python/doc/index.html

[4] http://www.swig.org/: http://www.swig.org/