LINUX.ORG.RU

Вызов Python функций из C++. boost::python, Pyhton C-API.

 , ,


1

1

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

Работаю над проектом, представляющим собой некую систему, собирающую данные с разных устройств. Данные предствлены в виде каналов. Устройства постоянно опрашиваются, данные в каналах обновляются. По происшествию каких-либо событий надо посылать команды на устройства.

Есть неоходимость ввести сценарии работы, определяющие эти события (events) и соответствующие обработчики (handlers). В качестве языка сценариев хочу использовать python.

Небольшое описание:

Сценарий будет представлять из себя python код, содержащийся, например в std::string строке. У него будет функия pyInit(), выполняемая при загрузке сценария. В pyInit() будем создавать события и биндовать на них обработчики. Далее в рабочем цикле C++ сервер будет запускать события, которые могут вызывать обработчики (если событие произошло).

Событие с точки зрения C++ - это объект, содержащий указатель на python функцию, определяющую это событие и указатель на python функцию определяющую обработчик.

На самом деле в C++ будет две основных категории событий cppEvents и pythonEvents. pythonEvents - это как раз те события, которые будут определяться в скрипте. cppEvents - это определенные в C++ события, такие как например eventValueChanged. Т.е. определяем eventBase от которого наследуем eventPython, eventChanged.

Так же в python нужно экспортировать некоторые функции, такие как auto getChannelValue(std::string chName) bool sendCmd(std::sting cmd) и т. д...

В качестве альтернативы python-у можно рассмотреть какой-нибудь другой язык сценариев, либо систему-плагинов, написанных на C++ и динамически подгружаемых. В случае плагинов получим быстродействие, в случае скриптов - простоту использования.

Вопросы:

  1. Как правильно передать в python скрипт? Те в каком виде у меня должен быть сценарий (например в виде std::string или файла)?
  2. Как вызвать функцию python из C++ кода, не завершая далее python окружения? Что бы в дальнейшем в этом же окружении вызвать другую.
  3. На сколько долгими будут вызовы python функций из C++ кода?
  4. Достаточно ли будет boost::python для реализации этого? Или придется рабоать с Python C-API? Хотелось бы по максммум использовать boost::python, по минимум Python C-API.

Везде описывают как создать C++ модуль для python. Мне же нужно из C++ кода вызывать python функйии, которые будут работать с моими C++ объектами.

Хотелось бы, что бы сценарий выглядел как то так: http://pastebin.com/2ZsiPeFT

В C++ все описано примерно так: http://pastebin.com/HHqbRRVx

Заранее благодарен за ответы!

ps: Если кому удобнее, то приведу код и тут.

# Пример сценария (как хотелось бы)

from mymodule import *
from subprocess import Popen, PIPE

# Вспомогательная функция. Запускает комманду в ОС.
def pExec(cmd):
    try:
        p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=False)
        return p
    except:
        print "Execute error"
        return NULL

# Глобальная переменная. На самом деле нужна static переменная в функии pyEventOne().
# Она будет использоваться для хранения предидущего значения между вызовами pyEventOne()
pVal = getChannelValue("ChName")

# Наше событие.
# Возвращяет true, если оно произошло.
# И если true, то при вызове eventBase->update() будет запущен handler - функция python
# Для простоты описано событие аналогичное eventChanged
def pyEventOne():
    newVal = getChannelValue("ChName")
    if (newVal == pVal):
        return False
    else:
        pVal = newVal
        return True

# Обработчик события.
# Вызывает экспотрированные из C++ методы, и может делать что-то еще.
def handlerOne():
    ev1.LockHanler()
    cppSendCmd("cmd...")
    ev2.UnlockHandler()

# еще один обработчик
def handlerTwo():
    cppSendCmd("cmd...")
    pExec("some system command")

# Функуия, вызываемая при инициализации сценария.
# Создает события, биндует обработчики.
def pyInit():
    # Создает событие (конструирует объект eventChanged)
    # и привязывает его к каналу данных с именем "SomeChannelName".
    # Возвращает объект ev1
    ev1 = CreateChEvent("SomeChannelName")

    # Конструирует объект eventPython
    ev2 = CreatePyEvent(pyEventOne)

    # Передаем в наши объекты указатели на функции обработчики
    ev1.SetHandler(handlerOne)
    ev2.SetHandler(handlerTwo)

    # Пока отключаем обработку события ev2,
    # что бы на него не реагировать раньше времени
    ev2.LockHandler()
//// С++ код (как то так реализованы event-ы):

class eventBase {
public:
  using eventHandler_t = std::function<void()>; /* для примера указал такой тип */
  eventBase();
  virtual ~eventBase();
  void setHandler(eventHandler_t h);
  void lockHandler();
  void unlockHandler();
  bool handlerLocked() const;
  bool update() { /* do something */ return this->run(); };
  int id() const;
protected:
  void runHandler();            /* call python function in python space */
private:
  eventHandler_t handler;       /* pointer to python function */
  bool handlerLock = false;
  virtual bool run() = 0;       /* event function */
  int _id = 0;
};

class eventChanged : public eventBase {
public:
  eventChanged(qc::Channel* ch);
  virtual ~eventChanged();
private:
  qc::Channel* _channel;
  virtual bool run() override;  /* true if value of channel has changed */
};

class eventPython : public eventBase {
public:
  using eventPythonFunc_t = std::function<bool()>; /* тоже для примера */
  eventPython();
  eventPython(eventPythonFunc_t);
  virtual ~eventPython();
  void setFunc(eventPythonFunc_t f) { evFunc = f; }
private:
  eventPythonFunc_t evFunc;
  virtual bool run() override;  /* run python event function, return true if event ocurred */
};

/* events collection. */
class eventsFld {
public:
  friend eventBase;
  static void update() { for (auto ev : events) ev->update(); }
  static auto size() { return events.size(); }
private:
  static std::list<eventBase*> events;
  static int lastid;
  static int  add(eventBase*);     /* use in eventBase::eventBase() */
  static bool remove(eventBase*);  /* use in eventBase::~eventBase() */
};

class scenario {
public:
  scenario(const std::string& );
  void init() {
    try {
      /* run pyInit(): */
      /*   make new events and bind handlers */
    } catch (...) {

    }
  }
private:
  std::string pyScript;
};

/* далее создаем сценарий  */
/* нинциализируем его */
/* а в рабочем цикле вызываем eventsFld::update() - обновляем все события */
★★

Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.