使用 Boost Python Numpy ndarray 作为 Class 成员变量

Using Boost Python Numpy ndarray as a Class member variable

我期待将 Python-Object 传递给 Boost Python Class。这个对象有一个 ndarray 作为属性,我想把这个 ndarray 作为私有成员变量存储在这个 Class 中,以便以后使用它。我找不到合适的方法来执行此操作,并且在将 boost::python::numpy::ndarray 变量声明为私有时出现编译器错误。

这是我当前的代码:

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

namespace p = boost::python;
namespace np = boost::python::numpy;

class FlatlandCBS {
  public:
    FlatlandCBS(p::object railEnv) : m_railEnv(railEnv) {

      // This is the Code I want to execute, so the ndarray is stored in the member varibale
      p::object distance_map = p::extract<p::object>(railEnv.attr("distance_map"));

      // Just a function which returns a ndarray (not the error)
      map = p::extract<np::ndarray>(distance_map.attr("get")());
    }

  private:
    p::object m_railEnv;
    // Can't use this and I get a Compiler Error
    np::ndarray map;
};


BOOST_PYTHON_MODULE(libFlatlandCBS) {       
  Py_Initialize();
  np::initialize();
  using namespace boost::python;

  class_<FlatlandCBS>("FlatlandCBS", init<object>());
}

生成的错误消息是:

error: no matching function for call to ‘boost::python::numpy::ndarray::ndarray()’

这也是我的 CMakeLists.txt 所以你可以重现这个错误:

cmake_minimum_required (VERSION 3.8)
project (libFlatlandCBS)


# Add all the files to the library so it can get created
ADD_LIBRARY(FlatlandCBS SHARED
                main.cpp)


# Set the Flags and the CXX command
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -Wall -Wextra -fconcepts")


INCLUDE_DIRECTORIES(include)

set(boostPython python)
find_package(PythonInterp 3.6 REQUIRED)
find_package(PythonLibs 3.6 REQUIRED)

include_directories(${PYTHON_INCLUDE_DIRS})

set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)  
set(Boost_USE_STATIC_RUNTIME OFF)
FIND_PACKAGE(Boost REQUIRED COMPONENTS system program_options numpy ${boostPython})


if(Boost_FOUND)

    include_directories(${Boost_INCLUDE_DIRS})
    target_link_libraries(FlatlandCBS ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})

else()
    message(FATAL_ERROR "Could not find boost.")
endif()

问题是 FlatlandCBS 的构造函数无效。 根据 cppreference:

Before the compound statement that forms the function body of the constructor begins executing, initialization of all direct bases, virtual bases, and non-static data members is finished. Member initializer list is the place where non-default initialization of these objects can be specified.

您得到的错误 - error: no matching function for call to ‘boost::python::numpy::ndarray::ndarray()’ - 是编译器告诉您它已尝试使用 ndarray 的默认构造函数。此 class 没有默认构造函数,因此您需要指定一个不同的构造函数以在初始化列表中使用(就像您为 m_railEnv 所做的一样!)。这里最简单的解决方案是将 map 的初始化移动到初始化列表中,如下所示:

    FlatlandCBS(p::object railEnv) :
        m_railEnv(railEnv),
        map(
          p::extract<np::ndarray>(
            p::extract<p::object>(
              railEnv.attr("distance_map")
            ).attr("get")()
          )
        )
    { }

这不是最漂亮的代码,但它应该可以工作。

所以在@unddoch 的帮助下,终于有可能解决这个问题。事实证明,连续使用两个extraxt函数是不行的,但不知何故,以下是可能的:

FlatlandCBS(p::object railEnv) :
   m_railEnv(railEnv),
   m_map(
     p::extract<np::ndarray>(
       railEnv
         .attr("distance_map")
       .attr("get")()
     )
   )

因此,如果您想从 python 对象中提取多个值,只需连续执行 .atrr() 个即可。