Pybind11:将字符串*参数传递给构造函数
Pybind11: passing a string* argument to a constructor
在不允许更改的 C++ 库中,我有一个如下所示的构造函数:
Dfa(const int n_state, const int dim_alf, const string *alf);
如果我简单地绑定
.def(py::init<const int, const int, const std::string*>())
编译成功。
问题是我无法通过 python 传递字符串 *,因为例如,如果我尝试在 python
上执行
alph=['x','y']
z=Dfa(3,2,alph)
它returns出现以下错误:
TypeError: __init__(): incompatible constructor arguments. The
following argument types are supported:
gi_gipy.Dfa(arg0: int, arg1: int, arg2: unicode)
用户 "R zu" 好心地建议我写一个包装器,但我不知道该怎么做。鉴于 python 中的内容类似于: ['x','y']
,
在 C++ 中被接受为 std::list<std::string>
,我尝试编写以下代码:
.def(py::init([](int n_state,int dim_alf, std::list<std::string> alph){
std::string* alfabeto=new std::string[dim_alf];
std::list<string>::iterator it=alph.begin();
for(int i=0;it!=alph.end();++i,++it) alfabeto[i]=*it;
Dfa::Dfa(n_state,dim_alf,alfabeto);
}))
但它 returns 给我 2 个错误:
cannot pass expression of type 'void' to variadic function
construct<Class>(v_h, func(std::forward<Args>(args)...)
和
static_assert failed "pybind11::init(): init function must return a compatible pointer,
holder, or value"
static_assert(!std::is_same<Class, Class>::value /* always false */
很明显,我对如何克服这个问题有点困惑,我认为这与使用指向字符串的指针作为构造函数的参数有关。我再说一遍,我无法更改库,我只能创建适当的绑定。感谢您的关注
main.cpp:
#include <iostream>
#include <list>
#include "pybind11/pybind11.h"
#include <pybind11/stl.h>
namespace py = pybind11;
class Dfa{
public:
Dfa(const int n_state, const std::size_t size, const std::string* alpha)
: alpha_(*alpha) {
std::cout << "n_state: " << n_state << "\n";
std::cout << "size: " << size << "\n";
std::cout << "*alpha: " << *alpha << "\n";
}
// copy the std::string, not the reference or pointer.
std::string alpha_;
};
Dfa make_dfa(int n_state, std::string alpha) {
// Copies the python unicode str to a local std::string
// Modifying the local copy won't change the python
// str.
return Dfa(n_state, alpha.size(), &alpha);
// Problem: Once the program leaves this function,
// This local std::string is destroyed.
// If the Dfa class just copies the pointer to this
// std::string instead of the std::string, the Dfa
// class will use a destroyed string.
// Unless the Dfa object copies the string, this will
// cause big trouble.
}
void print_char_list(std::list<char> alpha) {
for (auto c: alpha) std::cout << c << ", ";
std::cout << "\n";
std::cout << "length of list is: " << alpha.size() << "\n";
}
PYBIND11_MODULE(_cpp, m) {
py::class_<Dfa>(m, "Dfa")
.def_readwrite("alpha", &Dfa::alpha_);;
m.def("make_dfa", &make_dfa, "Create a Dfa object");
m.def("print_char_list", &print_char_list, "Print a list of chars");
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.9)
project(test_pybind11)
set(CMAKE_CXX_STANDARD 11)
# Find packages.
set(PYTHON_VERSION 3)
find_package( PythonInterp ${PYTHON_VERSION} REQUIRED )
find_package( PythonLibs ${PYTHON_VERSION} REQUIRED )
# Download pybind11
set(pybind11_url https://github.com/pybind/pybind11/archive/stable.zip)
set(downloaded_file ${CMAKE_BINARY_DIR}/pybind11-stable.zip)
file(DOWNLOAD ${pybind11_url} ${downloaded_file})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${downloaded_file}
SHOW_PROGRESS)
file(REMOVE ${downloaded_file})
set(pybind11_dir ${CMAKE_BINARY_DIR}/pybind11-stable)
add_subdirectory(${pybind11_dir})
include_directories(${pybind11_dir}/include)
# Make python module
pybind11_add_module(_cpp main.cpp)
Python 3 次测试:
>>> import _cpp
>>> s = "xyz"
>>> d = _cpp.make_dfa(1, s)
n_state: 1
size: 3
*alpha: xyz
>>> print(d.alpha)
xyz
>>> d.alpha = "abc"
>>> d.alpha
'abc'
>>> _cpp.print_char_list(['x', 'y', 'z'])
x, y, z,
length of list is: 3
在不允许更改的 C++ 库中,我有一个如下所示的构造函数:
Dfa(const int n_state, const int dim_alf, const string *alf);
如果我简单地绑定
.def(py::init<const int, const int, const std::string*>())
编译成功。 问题是我无法通过 python 传递字符串 *,因为例如,如果我尝试在 python
上执行alph=['x','y']
z=Dfa(3,2,alph)
它returns出现以下错误:
TypeError: __init__(): incompatible constructor arguments. The
following argument types are supported:
gi_gipy.Dfa(arg0: int, arg1: int, arg2: unicode)
用户 "R zu" 好心地建议我写一个包装器,但我不知道该怎么做。鉴于 python 中的内容类似于: ['x','y']
,
在 C++ 中被接受为 std::list<std::string>
,我尝试编写以下代码:
.def(py::init([](int n_state,int dim_alf, std::list<std::string> alph){
std::string* alfabeto=new std::string[dim_alf];
std::list<string>::iterator it=alph.begin();
for(int i=0;it!=alph.end();++i,++it) alfabeto[i]=*it;
Dfa::Dfa(n_state,dim_alf,alfabeto);
}))
但它 returns 给我 2 个错误:
cannot pass expression of type 'void' to variadic function
construct<Class>(v_h, func(std::forward<Args>(args)...)
和
static_assert failed "pybind11::init(): init function must return a compatible pointer,
holder, or value"
static_assert(!std::is_same<Class, Class>::value /* always false */
很明显,我对如何克服这个问题有点困惑,我认为这与使用指向字符串的指针作为构造函数的参数有关。我再说一遍,我无法更改库,我只能创建适当的绑定。感谢您的关注
main.cpp:
#include <iostream>
#include <list>
#include "pybind11/pybind11.h"
#include <pybind11/stl.h>
namespace py = pybind11;
class Dfa{
public:
Dfa(const int n_state, const std::size_t size, const std::string* alpha)
: alpha_(*alpha) {
std::cout << "n_state: " << n_state << "\n";
std::cout << "size: " << size << "\n";
std::cout << "*alpha: " << *alpha << "\n";
}
// copy the std::string, not the reference or pointer.
std::string alpha_;
};
Dfa make_dfa(int n_state, std::string alpha) {
// Copies the python unicode str to a local std::string
// Modifying the local copy won't change the python
// str.
return Dfa(n_state, alpha.size(), &alpha);
// Problem: Once the program leaves this function,
// This local std::string is destroyed.
// If the Dfa class just copies the pointer to this
// std::string instead of the std::string, the Dfa
// class will use a destroyed string.
// Unless the Dfa object copies the string, this will
// cause big trouble.
}
void print_char_list(std::list<char> alpha) {
for (auto c: alpha) std::cout << c << ", ";
std::cout << "\n";
std::cout << "length of list is: " << alpha.size() << "\n";
}
PYBIND11_MODULE(_cpp, m) {
py::class_<Dfa>(m, "Dfa")
.def_readwrite("alpha", &Dfa::alpha_);;
m.def("make_dfa", &make_dfa, "Create a Dfa object");
m.def("print_char_list", &print_char_list, "Print a list of chars");
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.9)
project(test_pybind11)
set(CMAKE_CXX_STANDARD 11)
# Find packages.
set(PYTHON_VERSION 3)
find_package( PythonInterp ${PYTHON_VERSION} REQUIRED )
find_package( PythonLibs ${PYTHON_VERSION} REQUIRED )
# Download pybind11
set(pybind11_url https://github.com/pybind/pybind11/archive/stable.zip)
set(downloaded_file ${CMAKE_BINARY_DIR}/pybind11-stable.zip)
file(DOWNLOAD ${pybind11_url} ${downloaded_file})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${downloaded_file}
SHOW_PROGRESS)
file(REMOVE ${downloaded_file})
set(pybind11_dir ${CMAKE_BINARY_DIR}/pybind11-stable)
add_subdirectory(${pybind11_dir})
include_directories(${pybind11_dir}/include)
# Make python module
pybind11_add_module(_cpp main.cpp)
Python 3 次测试:
>>> import _cpp
>>> s = "xyz"
>>> d = _cpp.make_dfa(1, s)
n_state: 1
size: 3
*alpha: xyz
>>> print(d.alpha)
xyz
>>> d.alpha = "abc"
>>> d.alpha
'abc'
>>> _cpp.print_char_list(['x', 'y', 'z'])
x, y, z,
length of list is: 3