Question

I am trying to expose this C++ class using Boost.Python:

class VAlgorithm {
public:

  VAlgorithm(const char *name);

  virtual ~VAlgorithm();

  virtual bool Initialize() = 0;
  virtual bool Process() = 0;
  virtual bool Finalize() = 0;

  virtual const char *GetName() const; // Default implementation in cpp file
}

My final goal is to define children of VAlgorithm in the python shell as python classes. Following this example, I defined a callback class:

class VAlgorithm_callback: public VAlgorithm {
public:
  VAlgorithm_callback(PyObject *p, const char *name) :
      self(p), VAlgorithm(name) {
  }
  const char * GetName() const {
    return call_method<const char *>(self, "GetName");
  }

  static const char * GetName_default(const VAlgorithm& self_) {
    return self_.VAlgorithm::GetName();
  }

private:
  PyObject *self;
};

Right now I am exposing only the class itself and the GetName() method. Since it is a virtual class, I placed this code inside BOOST_PYTHON_MODULE:

class_<VAlgorithm, VAlgorithm_callback, boost::noncopyable>("VAlgorithm", no_init) //
  .def("GetName", &VAlgorithm_callback::GetName_default); //

I can compile this and load the module in the python shell. Then I try to define a child class and call the GetName() default implementation defined in the C++ code:

>>> class ConcAlg1(VAlgorithm):
...   pass
... 
>>> c1 = ConcAlg1("c1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: This class cannot be instantiated from Python
>>> class ConcAlg2(VAlgorithm):
...   def __init__(self, name):
...     pass
... 
>>> c2 = ConcAlg2("c2")
>>> c2.GetName()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    VAlgorithm.GetName(ConcAlg2)
did not match C++ signature:
    GetName(VAlgorithm)
>>> class ConcAlg3(VAlgorithm):
...   def __init__(self, name):
...     super(ConcAlg3, self).__init__(self, name)
... 
>>> c3 = ConcAlg3("c3")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
RuntimeError: This class cannot be instantiated from Python

I am not an expert (just facing these issues for the first time), but it seems to me that ConcAlg1 and ConcAlg3 try to instantiate a VAlgorithm object and fail because of the no_init parameter used when exposing VAlgorithm (I Can't omit it or the code won't compile), and ConcAlg2 can't call GetName() because somehow it is not recognized as a child of VAlgorithm. I must be doing something trivially wrong but I can't figure out what (I am a novice of Boost.Python and extension). Can anyone please help me? Thanks

Was it helpful?

Solution

I have done things quite similar to this. Why don't you follow what you already have found in your comment ?

When you create an instance of a class deriving on VAlgorithm in Python, the VAlgorithm_callback will have to be instantiated in C++ to represent it. It's not possible if you don't declare any constructor in BOOST_PYTHON_MODULE.

Doing the way its done in the example should be ok:

class_<VAlgorithm, VAlgorithm_callback, boost::noncopyable>("VAlgorithm", init<std::string>())

When you define the init of the child class, you can use following syntax to call the parent constructor (but your code may work too, that's just the way I do). In the example you used, they don't even define init so the parent one is called instead.

class ConcAlg3(VAlgorithm):
    def __init__(self, name):
    VAlgorithm.__init__(self, name)

OTHER TIPS

I think I found the solution. Going through the example I quoted on my ask, and also the boost documentation here, I figured that the problem is that the callback class is the real class being wrapped by Boost.Python, and that it has to be a concrete class. So I implemented in it the missing pure virtual methods:

class VAlgorithm_callback: public VAlgorithm {
public:
  VAlgorithm_callback(PyObject *p, const char *name) :
      self(p), VAlgorithm(name) {
  }

  virtual bool Initialize() {
    return call_method<bool>(self, "Initialize");
  }
  virtual bool Process() {
    return call_method<bool>(self, "Process");
  }
  virtual bool Finalize() {
    return call_method<bool>(self, "Finalize");
  }

  const char * GetName() const {
    return call_method<const char *>(self, "GetName");
  }

// Supplies the default implementation of GetName
  static const char * GetName_default(const VAlgorithm& self_) {
    return self_.VAlgorithm::GetName();
  }

private:
  PyObject *self;
};

and also modified the wrapper as:

class_<VAlgorithm, boost::shared_ptr<VAlgorithm_callback>, boost::noncopyable ("VAlgorithm", init<const char *>()) //
 .def("Initialize", &VAlgorithm_callback::Initialize)
 .def("Process", &VAlgorithm_callback::Process)
 .def("Finalize", &VAlgorithm_callback::Finalize)
 .def("GetName", &VAlgorithm_callback::GetName_default);

Now I can define a child class in Python and call the default GetName() method:

>>> class ConcAlg(VAlgorithm):
...   pass
... 
>>> c = ConcAlg("c")
>>> c.GetName()
'c'
>>>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top