3

私はハイブリッドシステム(c ++、boost python)を持っています。私の C++ コードには、非常に単純な階層があります。

class Base{...}
class A : public Base{...}
class B : public Base{...}

さらに 2 つのビジネス (C++ 上) メソッド

smart_ptr<Base> factory() //this produce instances of A and B
void consumer(smart_ptr<A>& a) //this consumes instance of A

Pythonコードでは、でAのインスタンスを作成し、factoryコンシューマメソッドを呼び出そうとします:

v = factory() #I'm pretty sure that it is A instance
consumer(v)

絶対に合理的です私は例外を持っています:

consumer(Base) の Python 引数の型が C++ シグネチャと一致しませんでした: consumer(class A{lvalue})

これは、何らかの変換作業が必要であることを Boost に伝える方法がないために発生します。

動的キャスト動作を指定する方法はありますか? 前もって感謝します。

4

3 に答える 3

4

に関して言えばboost::shared_ptr、Boost.Pythonは一般的に必要な機能を提供します。この特定のケースでは、モジュール宣言がによって保持されるものを定義し、Boost.Pythonがから継承するように指示さto_pythonれている限り、カスタムコンバーターを明示的に提供する必要はありません。Baseboost::shared_ptr<Base>ABase

BOOST_PYTHON_MODULE(example) {
  using namespace boost::python;
  class_<Base, boost::shared_ptr<Base>, 
         boost::noncopyable>("Base", no_init);
  class_<A, bases<Base>,
         boost::noncopyable>("A", no_init);
  def("factory",  &factory);
  def("consumer", &consumer);
}

Boost.Pythonは、コアライブラリへの変更が必要なため、現在カスタム左辺値コンバーターをサポートしていません。したがって、関数はby値またはconst-referenceconsumerのいずれかを受け入れる必要があります。boost:shared_ptr<A>次のいずれかの署名が機能するはずです。

void consumer(boost::shared_ptr<A> a)
void consumer(const boost::shared_ptr<A>& a)

完全な例を次に示します。

#include <boost/python.hpp>
#include <boost/make_shared.hpp>

class Base
{
public:
  virtual ~Base() {}
};

class A
  : public Base
{
public:
  A(int value) : value_(value) {}
  int value() { return value_; };
private:
  int value_;
};

boost::shared_ptr<Base> factory()
{
  return boost::make_shared<A>(42);
}

void consumer(const boost::shared_ptr<A>& a)
{
  std::cout << "The value of object is " << a->value() << std::endl;
}

BOOST_PYTHON_MODULE(example) {
  using namespace boost::python;
  class_<Base, boost::shared_ptr<Base>, 
         boost::noncopyable>("Base", no_init);
  class_<A, bases<Base>,
         boost::noncopyable>("A", no_init);
  def("factory",  &factory);
  def("consumer", &consumer);
}

そして使用法:

>>> from example import *
>>> x = factory()
>>> type(x)
<class 'example.A'>
>>> consumer(x)
The value of object is 42
>>> 

Baseの基本クラスであるモジュール宣言が指定されているためA、Boost.Pythonはから返された型を解決できましfactory()example.A

于 2013-02-08T18:56:13.380 に答える
3

はいあります。独自の「from-python」コンバーターを宣言する必要があります。これは、boost.python のドキュメントで漠然と説明されています ( FAQ のこの回答を参照してください) が、このようなネット上のチュートリアルを見つけることができます。必要なことを行う最初の入力に基づいた完全な例を次に示します。

#include <boost/python.hpp>
#include <boost/make_shared.hpp>

class Base {
  public:
    virtual ~Base() {}
};

class A: public Base {
  public:
    A(int v): _value(v) {}
    int _value;
};

boost::shared_ptr<Base> factory() {
  return boost::make_shared<A>(27);
}

void consumer(boost::shared_ptr<A> a) {
  std::cout << "The value of object is " << a->_value << std::endl;
}

struct a_from_base {

  typedef typename boost::shared_ptr<A> container_type;

  // Registers the converter for boost.python
  a_from_base() {
    boost::python::converter::registry::push_back(&convertible, &construct,
        boost::python::type_id<container_type>());
  }

  // Determines convertibility: checks if a random 
  // object is convertible to boost::shared_ptr<A>
  static void* convertible(PyObject* ptr) {
    boost::python::object object(boost::python::handle<>(boost::python::borrowed(ptr)));
    boost::python::extract<boost::shared_ptr<Base> > checker(object);
    if (checker.check()) { //is Base
      boost::shared_ptr<Base> base = checker();
      if (boost::dynamic_pointer_cast<A>(base)) return ptr; //is A
    }
    return 0; //is not A
  }

  // Runs the conversion (here we know the input object *is* convertible)
  static void construct(PyObject* ptr, boost::python::converter::rvalue_from_python_stage1_data* data) {

    // This is some memory allocation black-magic that is necessary for bp
    void* storage = ((boost::python::converter::rvalue_from_python_storage<container_type>*)data)->storage.bytes;
    new (storage) container_type();
    data->convertible = storage;
    container_type& result = *((container_type*)storage); //< your object

    // The same as above, but this time we set the provided memory
    boost::python::object object(boost::python::handle<>(boost::python::borrowed(ptr)));
    boost::shared_ptr<Base> base = boost::python::extract<boost::shared_ptr<Base> >(object);
    result = boost::dynamic_pointer_cast<A>(base);
  }

};

// Your boost python module: compile it and run your test
// You should get "The value of object is 27".
BOOST_PYTHON_MODULE(convertible) {
  using namespace boost::python;
  class_<Base, boost::shared_ptr<Base>, boost::noncopyable>("Base", no_init);
  class_<A, boost::shared_ptr<A>, bases<Base>, boost::noncopyable>("A", no_init);
  a_from_base();
  def("factory", &factory);
  def("consumer", &consumer);
}

Bクラスの別の from-python コンバーターを作成するか、上記の構造をテンプレート化して のすべての子に対応することで、この例を拡張できますBase

于 2013-02-08T13:47:57.483 に答える
3

このスレッドで説明されているように、自動 (または手動) の from-python コンバーターを使用して問題を解決することはできません。Python から期待どおりに動作するように、動的な型の動作をエミュレートしてパッチを適用する必要があります。非 const 参照を渡しているため、consumer()メソッド内で変更する必要があると思います。

これは完全に機能する例です。C++ 配管は、Python で発生した、Baseまたは aのすべての可能な派生を知る必要があります。TypeErrorハンズオン:

#include <boost/python.hpp>
#include <boost/make_shared.hpp>

class Base {
  public:
    Base(int value) : value_(value) {}
    virtual ~Base() {}
    int value() const { return value_; };
    void value(int value) { value_ = value; }
  private:
    int value_;
};

class A : public Base {
  public:
    A(int value): Base(value) {}
};

class B : public Base {
  public:
    B(int value): Base(value) {}
};

class C : public Base {
  public:
    C(int value): Base(value) {}
};

boost::shared_ptr<Base> factory(boost::python::str choose) {
  if (choose == "a") return boost::make_shared<A>(1);
  else if (choose == "b") return boost::make_shared<B>(10);
  else return boost::make_shared<C>(100);
}

void consumer_a(boost::shared_ptr<A>& a) {
  std::cout << "The value of object was " << a->value() << std::endl;
  a = boost::make_shared<A>(a->value()+1);
  std::cout << "The new value of object is " << a->value() << std::endl;
}

void consumer_b(boost::shared_ptr<B>& b) {
  std::cout << "The value of object is " << b->value() << std::endl;
  b = boost::make_shared<B>(b->value()+1);
  std::cout << "The new value of object is " << b->value() << std::endl;
}

void consumer_python(boost::shared_ptr<Base>& base) {
  //try the first one
  boost::shared_ptr<A> a = boost::dynamic_pointer_cast<A>(base);
  if (a) {
    consumer_a(a);
    base = a; ///< don't forget the assignment here
    return;
  }

  //try the second one
  boost::shared_ptr<B> b = boost::dynamic_pointer_cast<B>(base);
  if (b) {
    consumer_b(b);
    base = b; ///< don't forget the assignment here
    return;
  }

  //we leave C uncovered to see what happens ;-)

  //if you get here, you can raise an exception for python
  PyErr_Format(PyExc_TypeError, "Input type is neither A or B");
  throw boost::python::error_already_set();
}

//notice you don't even need to declare a binding for A, B or C
BOOST_PYTHON_MODULE(example) {
  using namespace boost::python;
  class_<Base, boost::shared_ptr<Base>, boost::noncopyable>("Base", no_init);
  def("factory",  &factory);
  def("consumer", &consumer_python);
}

コンパイル後、このスクリプトを実行して動作を確認できます。

import example
print "Creating object of type A..."
a = example.factory("a")
print "Consuming A twice..."
example.consumer(a)
example.consumer(a)

print "Creating object of type B..."
b = example.factory("b")
print "Consuming B twice..."
example.consumer(b)
example.consumer(b)

print "Creating object of type C..."
c = example.factory("c")
print "Trying to consume (uncovered) C..."
example.consumer(c)

出力は次のようになります。

Creating object of type A...
Consuming A twice...
The value of object was 1
The new value of object is 2
The value of object was 2
The new value of object is 3
Creating object of type B...
Consuming B twice...
The value of object is 10
The new value of object is 11
The value of object is 11
The new value of object is 12
Creating object of type C...
Trying to consume (uncovered) C...
Traceback (most recent call last):
  File "test.py", line 25, in <module>
    example.consumer(c)
TypeError: Input type is neither A or B
于 2013-02-09T05:44:31.273 に答える