使用 python 调用 C++ 函数

Calling C++ function using python

我正在尝试为一个用 C++ 实现的函数构建 python 包装器,该函数接受 2d 向量和 returns 2d 向量。我正在尝试调整 this 中的代码以满足我的需要。

Input matrix shape: (n*2)  
Output matrix shape: (n*2)

我认为 code.i 文件有问题,但不太确定问题到底是什么。
如果有任何其他库可以实现这一点,那么也可以。我只需要从 python.
调用 c++ 函数 这是完整的可重现示例:Google Colab
我的文件:
code.cpp:

#include <vector>
#include "geomutils.h"
#include "code.h"

using namespace std;

vector< vector<double> > computeConvexHull(vector< vector<double> > i_matrix){
  Polygon custompts, customhull;
  for (int r = 0; r < i_matrix.size(); r++){
    custompts.push_back(Point(i_matrix[r][0], i_matrix[r][1]));
  }
  computeConvexHull(custompts, customhull);
  vector< vector<double> > res;
  for(int i = 0;i < customhull.size();i ++) {
        res[i][0] = customhull[i].x;
        res[i][1] = customhull[i].y;
  }
  return res;
}

geomutils.cpp:

#include "geomutils.h"

#include <iostream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)


void computeConvexHull(Polygon &pts, Polygon &chull) {
    chull.clear();
    if(pts.size() == 1) {
        chull.push_back(pts[0]);
        chull.push_back(pts[0]);
        return;
    } else if(pts.size() == 2) {
        chull.push_back(pts[0]);
        chull.push_back(pts[1]);
        chull.push_back(pts[0]);
        return;
    }

    typedef boost::tuple<double, double> point;
    typedef boost::geometry::model::multi_point<point> mpoints;
    typedef boost::geometry::model::polygon<point> polygon;

    mpoints mpts;

    for(int i = 0;i < pts.size();i ++) {
        boost::geometry::append(mpts,point(pts[i].x,pts[i].y));
    }
    polygon hull;

    // Polygon is closed
    boost::geometry::convex_hull(mpts, hull);
    for(auto pt : hull.outer()) {
        chull.push_back(Point(pt.get<0>(), pt.get<1>()));
    }
}

geomutils.h:

#ifndef GEOMUTILS_H
#define GEOMUTILS_H

#include <vector>

struct Point {
    double x,y;

    Point(){}
    Point(double x, double y):x(x),y(y){}
};

typedef std::vector<Point> Polygon;

void computeConvexHull(Polygon &pts, Polygon &chull);

#endif // GEOMUTILS_H

code.i:

%module code
%{
#include "code.h"
#include "geomutils.h"
%}
%include "std_vector.i"
namespace std {

  /* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */
  %template(VecDouble) vector< vector<double> >;
  %template(VecVecdouble) vector< vector<double> >;
}

%include "code.h"

该代码使用了一个名为 boost 的外部库。我尝试生成包装器的方式如下:

g++ -c -fPIC code.cpp geomutils.cpp

swig -c++ -python code.i

g++ -c -fPIC code_wrap.cxx  -I/usr/include/python3.7 -I/usr/lib/python3.7

前两个命令运行没有给出任何错误,但第三个代码给我一个错误。
问题中的错误太长 post。但是可以使用代码 link 重现该错误。 更新 1:
在尝试了答案 1 中建议的更改和 运行 宁以下命令后,代码编译但当我尝试 运行 来自 python 的 c++ 代码时没有任何反应。代码在同一个 link 中更新。 “test.py”是调用 computeConvexHull 函数的简单 python 代码。
命令:

g++ -c -fPIC code.cpp geomutils.cpp
swig -c++ -python code.i
g++ -c -fPIC code_wrap.cxx  -I/usr/include/python3.7 -I/usr/lib/python3.7
g++ -shared -Wl,-soname,_code.so -o _code.so code.o geomutils.o code_wrap.o
python test.py

首先,我只想说这是我在 SO 上看到的最多 well-written 的问题之一。非常感谢。

问题是您正在为同一类型定义一个模板两次,您不允许这样做as-per SWIG documentation:

The %template directive should not be used to wrap the same template instantiation more than once in the same scope. This will generate an error. For example:

%template(intList) List<int>;
%template(Listint) List<int>;    // Error.   Template already wrapped.

This error is caused because the template expansion results in two identical classes with the same name. This generates a symbol table conflict. Besides, it probably more efficient to only wrap a specific instantiation only once in order to reduce the potential for code bloat.

Since the type system knows how to handle typedef, it is generally not necessary to instantiate different versions of a template for typenames that are equivalent. For instance, consider this code:

%template(intList) vector<int>;
typedef int Integer;
...
void foo(vector<Integer> *x);

In this case, vector is exactly the same type as vector. Any use of Vector is mapped back to the instantiation of vector created earlier. Therefore, it is not necessary to instantiate a new class for the type Integer (doing so is redundant and will simply result in code bloat).

这个问题正如您所怀疑的那样,在 code.i 中,您将 VecDoubleVecVecDouble 模板化为 vector<vector<double>>

根据 this answer,我猜你的意思是:

  %template(VecDouble) vector<double>;
  %template(VecVecdouble) vector< vector<double> >;

确实可以编译。

主要问题是这条不正确的行:

%template(VecDouble) vector<vector<double>>;  // Should be vector<double>

这里有一个最小的例子,演示了二维向量被适当地包装在一个接口文件中:

test.i

%module test

%include "std_vector.i"
%template(VecDouble) std::vector<double>;
%template(VecVecdouble) std::vector<std::vector<double>>;

%inline %{
#include <vector>

std::vector<std::vector<double>> computeConvexHull(std::vector<std::vector<double>> i_matrix) {
    for(auto& m : i_matrix)
        for(auto& n : m)
            n *= 2.0;
    return i_matrix;
}
%}

演示:

>>> import test
>>> test.computeConvexHull([[1.25,2.5,3.75],[4.25,5.5,6.75]])
((2.5, 5.0, 7.5), (8.5, 11.0, 13.5))