使用boost :: python将虚拟成员函数从C ++暴露给Python

时间:2015-11-02 11:42:04

标签: python c++ boost boost-python

我尝试向python公开两个不同的类,但是我没有让它编译。我试着按照boost :: python示例,它运行得很好。但是,如果我尝试为我的类编写包装类,它就不起作用了。我在下面提供了两个最小的例子:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;

public class exceltst 
{
    static void xls(File inputFile, File outputFile,int sheet_num) 
    {
        // For storing data into CSV files
        StringBuffer data = new StringBuffer();
        try 
        {
        FileOutputStream fos = new FileOutputStream(outputFile);

        // Get the workbook object for XLS file
        HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(inputFile));
        // Get first sheet from the workbook
        HSSFSheet sheet = workbook.getSheetAt(sheet_num);
        Cell cell; 
        Row row;

        // Iterate through each rows from first sheet
        Iterator<Row> rowIterator = sheet.iterator();
        while (rowIterator.hasNext()) 
        {
                row = rowIterator.next();
                // For each row, iterate through each columns
                Iterator<Cell> cellIterator = row.cellIterator();
                while (cellIterator.hasNext()) 
                {
                        cell = cellIterator.next();

                        switch (cell.getCellType()) 
                        {
                        case Cell.CELL_TYPE_BOOLEAN:
                                data.append(cell.getBooleanCellValue() + ",");
                                break;

                        case Cell.CELL_TYPE_NUMERIC:
                                data.append(cell.getNumericCellValue() + ",");
                                break;

                        case Cell.CELL_TYPE_STRING:
                                data.append(cell.getStringCellValue() + ",");
                                break;

                        case Cell.CELL_TYPE_BLANK:
                                data.append("" + ",");
                                break;

                        default:
                                data.append(cell + ",");
                        }


                }
                data.append('\n');
        }

        fos.write(data.toString().getBytes());
        fos.close();
        }
        catch (FileNotFoundException e) 
        {
                e.printStackTrace();
        }
        catch (IOException e) 
        {
                e.printStackTrace();
        }
        }

        public static void main(String[] args) 
        {
                File inputFile = new File("C:\\Call_Center_20150323.xls");
                File outputFile1 = new File("C:\\live_person.csv");
                xls(inputFile, outputFile1,3);
        }
 }

struct Base
{
    virtual ~Base() {}
    virtual std::unique_ptr<std::string> f() = 0;
};

struct BaseWrap : Base, python::wrapper<Base>
{
    std::unique_ptr<std::string> f()
    {
        return this->get_override("f")();
    }
};

第一个没有编译,因为唯一的指针(我认为boost :: python不使用唯一的指针?),第二个例子抱怨void函数内的return语句。有人可以帮我解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

示例无法编译,因为:

  • 第一个示例尝试将未指定的类型(override::operator()的返回类型)转换为不兼容的类型。特别是,Boost.Python目前不支持std::unique_ptr,因此不会转换为它。
  • 当调用函数声明它返回void时,第二个示例尝试返回上面提到的未指定类型。

从Python的角度来看,字符串是不可变的,并且尝试将字符串的所有权从Python转移到C ++会违反语义。但是,可以在C ++中创建字符串的副本,并将复制的字符串的所有权传递给C ++。例如:

std::unique_ptr<std::string> BaseWrap::f()
{
  // This could throw if the Python method throws or the Python
  // method returns a value that is not convertible to std::string.
  std::string result = this->get_override("f")();

  // Adapt the result to the return type.
  return std::unique_ptr<std::string>(new std::string(result));
}

this->get_override("f")()返回的对象具有未指定的类型,但可用于转换为C ++类型。如果Python抛出,则抛出override的调用,如果从Python返回的对象不能转换为C ++类型,则抛出转换为C ++类型。

这是一个完整的示例demonstrating两种方法来使返回的Python对象适应C ++对象。如上所述,可以使用override转换。或者,可以使用boost::python::extract<>,允许用户在执行转换之前检查转换是否会失败:

#include <memory> // std::unique_ptr
#include <boost/algorithm/string.hpp> // boost::to_upper_copy
#include <boost/python.hpp>

struct base
{
  virtual ~base() {}
  virtual std::unique_ptr<std::string> perform() = 0;
};

struct base_wrap : base, boost::python::wrapper<base>
{
  std::unique_ptr<std::string> perform()
  {
    namespace python = boost::python;
    // This could throw if the Python method throws or the Python
    // method returns a value that is not convertible to std::string.
    std::string result = this->get_override("perform")();

    // Alternatively, an extract could be used to defer extracting the
    // result.
    python::object method(this->get_override("perform"));
    python::extract<std::string> extractor(method());

    // Check that extractor contains a std::string without throwing.
    assert(extractor.check());

    // extractor() would throw if it did not contain a std::string.
    assert(result == extractor());

    // Adapt the result to the return type.
    return std::unique_ptr<std::string>(new std::string(result));
  }
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<base_wrap, boost::noncopyable>("Base", python::init<>())
    .def("perform", python::pure_virtual(&base::perform))
    ;

  python::def("make_upper", +[](base* object) {
    auto result = object->perform(); // Force dispatch through base_wrap.
    assert(result);
    return boost::to_upper_copy(*result);
  });
}

交互式使用:

>>> import example
>>> class Derived(example.Base):
...     def perform(self):
...         return "abc"
...        
>>> derived = Derived()
>>> assert("ABC" == example.make_upper(derived))
相关问题