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

в 14:21, , рубрики: boost, c plus plus, c++, python, websockets, Блог компании PENXY, метки: , , ,

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

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

При написании кода использовалась питоновская библиотека Websocket от Autobahn (http://www.tavendo.de/autobahn/clientlibraries.html), которую было необходимо вызывать из C++. Для этих целей в Python предусмотрен Python C-API (http://docs.python.org/extending/index.html), однако многие простые действия, например, вызов функций делается несколькими действиями. После небольшого гугления был найден ряд библиотек, позволяющих упростить подобные действия: Boost.Python (http://www.boost.org/doc/libs/1_39_0/libs/python/doc/index.html), SWIG(http://www.swig.org/), 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

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js