从包装器中提取到矢量地图

时间:2017-02-24 00:28:18

标签: c++ stl containers boost-python

上下文是嵌入式python脚本并调用解释器 来自用C ++编写的代码。

下面的C ++和Python代码总结了用C ++包装的技术,在Python中使用,然后在C ++中提取两个最常见的STL容器:

  • 的std ::矢量
  • 的std ::地图

它更专注于它们持有std :: string类型元素的情况。

对于不熟悉boost python库的编码人员来说,其中一个难点是,增强迭代器和可迭代套件正在映射这两种语言共有的概念,但在两个相应的编程环境中它们的处理方式却截然不同。

借鉴现有的材料和个人实验,我已经向自己澄清了最常见的用途。没有其他地方没有显示,但它确实感觉就像boost :: python在示例和说明案例方面可以容忍一些冗余。

有一种情况我到目前为止似乎无法解决:将包装的字符串向量返回给C ++。如果有人能够在这方面让我高兴,我将不胜感激。

该代码旨在自包含,有两个目的。首先,它旨在帮助那些像我一样寻找更多简单用例的人。它还表明,操纵double的向量比向量字符串更直接。

在下面的C ++示例中,我得到一个运行时错误,基本上说: typedef std :: vector VecStr; class boost :: python :: class>来自此类型为MapStringVectorString的Python对象的struct boost :: python :: deltail :: not_specified_struc。

任何建议都将不胜感激。

PS:我不得不从内存重构修改后的代码,可能会出现一些语法错误。一旦我可以访问连接的机器,我就会纠正它们。

#include "stdafx.h"
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <Python.h>
#include <string>
#include <iostream>
#include <vector>

using namespace std;
using namespace boost::python;

BOOST_PYTHON_MODULE(idctor)
{
    typedef std::vector<string> VectorString;
    typedef std::map<string, VectorString> MapVectorString;
    typedef class_<VectorString, shared_ptr<VectorString>> pyVectorString;
    using namespace boost::python;
    class_<std::vector<double> >("DoubleVector")
        .def(vector_indexing_suite<std::vector<double> >());

    class_<std::vector<std::string>,shared_ptr<VectorString> >("VectorString")
        .def(vector_indexing_suite<std::vector<string> >());

    class_<std::map<std::string, double> >("StringDoubleMap")
        .def(map_indexing_suite<std::map<std::string, double> >());

    class_<std::map<string, string> >("MapStringString")
        .def(map_indexing_suite<std::map<std::string, string> >());

    class_<std::map<string, VectorString> , shared_ptr<MapVectorString>>("MapStringVectorString")
        .def(map_indexing_suite<std::map<std::string, VectorString> >());
}


typedef std::vector<string> VectorString;
typedef  class_<VectorString> pyVectorString;
typedef class_<VectorString, shared_ptr<VectorString>> pyMapVectorString;


int main()
{
    PyImport_AppendInittab("idctor", &PyInit_idctor);
    Py_Initialize();
    try {

        //PyInit_hello();
        object main
            = object(handle<>(borrowed(PyImport_AddModule("__main__"))));
        object main_namespace = main.attr("__dict__");
        PyRun_SimpleString("import sys\n");
        exec_file("G:\\Developments\\VisualStudio\\BoostPythonSTLContainers\\stlcontainers.py", main_namespace);
        //       Getting back an Python object containing a 
        //            vector of double, extracting the 
        //            corresponding typed C++ object and using it
        object pyv = main_namespace["v"];
        //---------------->  this works <--------------
        std::vector<double>& v = extract<vector<double>&>(pyv);
        v.push_back(665);
        //----------------> this does not <----------------
        //       Getting back a working Python object 
        object pyvs = main_namespace["vs"];
        //   Attempting to convert it back to its true C++ type
        //    Here the conversion fails:
        VectorString& vs = extract<VectorString&>(pyvs);
        vs.push_back("not yet the beast");

        //   Attempt to do the same with a map of string to vector of strings
        object pymvs = main_namespace["mvs"];

        //----------------> Clearly this fails too <--------------
        pyMapVectorString& mvs = extract<pyMapVectorString&>(pymvs);
        object pyvs = mvs["this "];
        VectorString& vs = extract<VectorString&>(pyvs);
        vs.push_back({ "should ","work" });
    }
    catch (error_already_set) {
        PyErr_Print();
        return 1;
    }

    Py_Finalize();
    return 0;
}

这是(工作)python代码:

# 
#    All the python code below is working fine
#         (the issue is converting back in C++)
import idctor
#defining a function to print a container
def tostr(container):
    string ='['
    for i in container:
        string += str(i)+","
    string+='end]'
    return string

# Turning it into a instance method
idctor.DoubleVector.__str__ = tostr

# instantiating a vector of doubles
v = idctor.DoubleVector()
v.append(1.0)
v.append(2.0)
for i in v:
    print(i)

#instantiating a vector of strings
idctor.VectorString.__str__ = tostr
vs = idctor.VectorString()
vs.append("he2")
vs.append("he1")
print("Directly: ", vs)
for s in vs:
    print(s)
m = idctor.StringDoubleMap()

# instantiating a map of string to doubles
m["a"] = 1
m["b"] = 2
for i in m:
    print(i)
print(m)

#instantiating a map of string to strings
ms = idctor.MapStringString()
ms["a"]="he1"
ms["b"]="he2"
for s in ms:
    print(s)

#instantiating a map of string to vectors of strings
mvs = idctor.MapStringVectorString()
mvs["a"]=vs 
vs2=idctor.VectorString()
vs2.append("cy")
vs2.append("ve")
mvs["b"]=vs2
for vs in mvs:
    print(vs)

1 个答案:

答案 0 :(得分:1)

可以通过在类的模板声明中输入NoProxy = true选项来纠正第一个问题。第二个问题仍然存在,似乎应该构建一个来自python的特殊转换器来处理这种情况。我还没弄明白该怎么做。然而,我发现依赖于python对象本身的 - 可以说是丑陋的 - 解决方法。只是添加以防有些人想要遵循这条道路。

BOOST_PYTHON_MODULE(idctor)
 {
 typedef std : : vector <string> VectorString;
 typedef std::map<string, VectorString> MapVectorString;
 typedef class_<Vector String, shared_ptr<VectorString>> pyVectorString;
 using namespace boost::python;

 //                    NOTICE THE TRUE IN THE 
//          vector_indexing_suite second template argument
 class_<std::vector <std::string>, Shared_ptr <VectorString> > ("VectorString")
.def(vector_indexing_suite<std::vector<string>, true >());


 class <std::map<std::string, double> > ("StringDoubleMap")
.def(map_indexing_suite<std::map<std::string, double>, true >());

 class <std::map<string, string> > ("MapStringString")
.def(map indexing suite<std::map<string, string>, true >());

 class <MapVectorString, shared ptrkMapVectorString>, boost::noncopyable>("MapStringVectorString")
.def(map_indexing_suite<MapVectorString, true >());

 }

 typedef std::vector<string> VectorString;
 typedef class_<VectorString> pyVectorString;
 typedef std::map<string, VectorString> MapVectorString;
 typedef class_<MapVectorString, shared_ptr<MapVectorString>> pyMapVectorString;

int main()
{

PyImport_AppendInittab ("idctor", &PyInit_idctor) ;
Py_Initialize();
try {
object main
= object (handle<> (borrowed (Pylimport_AddModule ("__main__"))));
object main_namespace = main.attr("__dict__");
exec_file ("WrappedSTLContainer.py", main_namespace);

// Getting back a vector of double in C++ and using it
object pyv = main_namespace ["v"];
//- - - - - - - - - - - - - - - - > this works <- - - - - - - - - - - - - -
std::vector<double>& v = extract<vector<double>&>(pyv);
v.push_back(665) ;
// Getting back a vector of string in C++ and attempting to use it
object pyvs = main namespace["vs"];
//- - - - - - - - - - - - - - - - > this works now <- - - - - - - - - - - - - -
std::vector<std::string>& vs = extract<vector<string>&>(pyvs);
vs.push_back("Almost the Beast");
cout << "From C++ this time -> vs[2] " << vs[2] << endl;

// Getting back a map of string-vector of string in C++
// and trying to use it
object pymvs = main namespace["mvs"];
//- - - - - - - - - - - - - - - - > this still does not work - - - - - - - - - - - - - -
// pyMapVectorString& mvs = extract< pyMapVectorString&> (pymvs);

//------------------------->  Work around: working in python
 object method = pymvs.attr("__setitem__");
 VectorString vs3;
 vs3.push_back(" should ");
 vs3.push_back("work");
 object ignored = method ("this", vs3);
 const char * s = extract<const char *> (pymvs.attr("__getitem__")("this ").attr ("__str__") ());
 cout << s  << endl;
 }
 catch (error_already_set) { 
   PyErr_Print();
   return 1;
 }

 Py_Finalize();
 return 0;
 }