Предоставление экземпляра класса C ++ встроенному интерпретатору Python - PullRequest
17 голосов
/ 22 ноября 2011

Я ищу простой способ предоставления экземпляра класса C ++ встроенному интерпретатору Python.

  • У меня есть библиотека C ++.Эта библиотека упакована (сейчас используется swig), и я могу использовать ее из интерпретатора python
  • У меня есть основная программа на C ++, которая создает класс Foo из моей библиотеки и встраивает интерпретатор python

Я хотел бы представить свой экземпляр Foo в мире C ++ миру Python (и рассматривается как класс Foo).

Возможно ли это, если да, то как?

Я думаю, что это почти как в первом ответе: boost :: python :: ptr или PyInstance_New

Я думаю, это означает, что я должен использоватьboost.Python обернуть мою библиотеку?

Моя единственная цель - манипулировать моим экземпляром Foo в C ++ во встроенном интерпретаторе python (не уверен, что это можно сделать с помощью предыдущего метода).

Надеюсь, я в порядке, спасибо за вашу помощь.

update

Спасибо за ваши ответы.Фактически, я уже выставил свой класс Foo на python (с помощью swig).

Что у меня есть:

мой класс Foo:

class Foo{...};

моя обернутая библиотека (включая класс Foo), доступная для python: , поэтому я могу запустить интерпретатор python и сделать что-то вроде этого:

import my_module
foo=my_modulde.Foo()

Что я хочу:

Наличие главной программы на C ++, которая встраивает интерпретатор python и манипулирует переменными мира C ++.

int main(int argc, char **argv)
{
    Foo  foo;   // instanciates foo

    Py_Initialize();

    Py_Main(argc, argv); // starts the python interpreter
                         // and manipulates THE foo instance in it

    Py_Finalize();

    return 0;
}

Понятнее?:)

Ответы [ 3 ]

17 голосов
/ 22 ноября 2011

Boost python Позволяет вам представлять классы c ++ для python очень тесно интегрированным способом - вы можете даже обернуть их, чтобы вы могли извлекать классы python из ваших классов c ++ и иметь виртуальные методы, разрешенные вPython переопределяет.

Хорошее место для начала - Boost Python. .


edit:

Youможет создать объект c ++ и передать ссылку на него внутреннему интерпретатору python, например:

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/python.hpp>
#include <string>
#include <iostream>

namespace bp = boost::python;

struct Foo{
    Foo(){}
    Foo(std::string const& s) : m_string(s){}
    void doSomething() {
        std::cout << "Foo:" << m_string << std::endl;
    }
    std::string m_string;
};

typedef boost::shared_ptr<Foo> foo_ptr;

BOOST_PYTHON_MODULE(hello)
{
    bp::class_<Foo, foo_ptr>("Foo")
        .def("doSomething", &Foo::doSomething)
    ;
};

int main(int argc, char **argv)
{
    Py_Initialize();
    try {
        PyRun_SimpleString(
            "a_foo = None\n"
            "\n"
            "def setup(a_foo_from_cxx):\n"
            "    print 'setup called with', a_foo_from_cxx\n"
            "    global a_foo\n"
            "    a_foo = a_foo_from_cxx\n"
            "\n"
            "def run():\n"
            "    a_foo.doSomething()\n"
            "\n"
            "print 'main module loaded'\n"
        );

        foo_ptr a_cxx_foo = boost::make_shared<Foo>("c++");

        inithello();
        bp::object main = bp::object(bp::handle<>(bp::borrowed(
            PyImport_AddModule("__main__")
        )));

        // pass the reference to a_cxx_foo into python:
        bp::object setup_func = main.attr("setup");
        setup_func(a_cxx_foo);

        // now run the python 'main' function
        bp::object run_func = main.attr("run");
        run_func();
    }
    catch (bp::error_already_set) {
        PyErr_Print();
    }

    Py_Finalize();

    return 0;
}
2 голосов
/ 16 августа 2017

Для справки вот как вы можете добиться этого, используя pybind11 :

#include <pybind11/pybind11.h>
#include <iostream>

namespace py = pybind11;

class Foo
{
public:
    Foo(const std::string &s) : s_(s) {}
    void doSomething() { std::cout << s_ << std::endl; }

private:
    std::string s_;
};

typedef std::shared_ptr<Foo> FooPtr;

PYBIND11_PLUGIN(FooModule)
{
    py::module m("FooModule");
    py::class_<Foo, FooPtr>(m, "Foo")
        .def("doSomething", &Foo::doSomething);
    return m.ptr();
}

int main(int argc, char **argv)
{
    // Create Foo instance in C++
    FooPtr foo = std::make_shared<Foo>("Hello, World!");

    // Initialize interpreter and import module defining Foo wrapper
    PyImport_AppendInittab("FooModule", PyInit_FooModule);
    Py_Initialize();
    PyRun_SimpleString("import FooModule");

    // Make Foo instance accessible in python
    py::module main = py::module::import("__main__");
    main.attr("foo") = foo;

    // Test that it works
    PyRun_SimpleString("foo.doSomething()");

    // Finalize
    Py_Finalize();
    return 0;
}
1 голос
/ 15 июня 2018

Я знаю, что это старый вопрос, но вот решение с использованием SWIG.

foo.h:

#pragma once
#include <string>

struct Foo{
  Foo();
  Foo(std::string const& s);
  void doSomething();
  std::string m_string;
};

foo.cpp:

#include "foo.h"
#include <iostream>

Foo::Foo() {}

Foo::Foo(std::string const& s) : m_string(s) {}

void Foo::doSomething() {
  std::cout << "Foo:" << m_string << std::endl;
}

foo.i:

%module module
%{
  #include "foo.h"
%}

%include "std_string.i"
%include "foo.h"

Генерация обычной SWIG-оболочки вместе со средой выполнения

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

Генерация модуля SWIG, содержащего struct Foo:

g++ -fPIC -Wall -Wextra -shared -o _module.so foo_wrap.cxx foo.cpp -I/usr/include/python2.7 -lpython2.7

Если вы хотите обмениваться информацией о типах между несколькими модулями, можно добавить аргумент -DSWIG_TYPE_TABLE=SomeName.

Теперь вот как экземпляр C ++ Foo передается интерпретатору

#include "foo.h"
#include <Python.h>
#include "runtime.h"

int main(int argc, char **argv) {
  Py_Initialize();

  PyObject* syspath = PySys_GetObject((char*)"path");
  PyObject* pName = PyString_FromString((char*) ".");
  int err = PyList_Insert(syspath, 0, pName);
  Py_DECREF(pName);

  err = PySys_SetObject((char*) "path", syspath);

  PyObject *main, *module, *pInstance, *run, *setup;

  try {
    main = PyImport_ImportModule("__main__");
    err = PyRun_SimpleString(
        "a_foo = None\n"
        "\n"
        "def setup(a_foo_from_cxx):\n"
        "    print 'setup called with', a_foo_from_cxx\n"
        "    global a_foo\n"
        "    a_foo = a_foo_from_cxx\n"
        "\n"
        "def run():\n"
        "    a_foo.doSomething()\n"
        "\n"
        "print 'main module loaded'\n");

    // Load Python module
    module = PyImport_ImportModule("module");

    swig_type_info *pTypeInfo = nullptr;
    pTypeInfo = SWIG_TypeQuery("Foo *");

    Foo* pFoo = new Foo("Hello");
    int owned = 1;
    pInstance =
        SWIG_NewPointerObj(reinterpret_cast<void*>(pFoo), pTypeInfo, owned);

    setup = PyObject_GetAttrString(main, "setup");

    PyObject* result = PyObject_CallFunctionObjArgs(setup, pInstance, NULL);
    Py_DECREF(result);

    run = PyObject_GetAttrString(main, "run");

    result = PyObject_CallFunctionObjArgs(run, NULL);
    Py_DECREF(result);
  }
  catch (...) {
    PyErr_Print();
  }

  Py_DECREF(run);
  Py_DECREF(setup);
  Py_DECREF(pInstance);
  Py_DECREF(module);
  Py_DECREF(main);

  Py_Finalize();
  return 0;
}

Выше может быть скомпилировано:

g++ -Wall -Wextra -I/usr/include/python2.7 main.cpp foo.cpp -o main -lpython2.7
...