8

我想[1,2,3,4]从 C++ 脚本中获取一个 python 列表,比如说,。我编写了 C++ 脚本,它返回一个向量。

如何在没有 SWIG/SIP/Cython/和其他的情况下连接末端?

将 C++ 编译为 .exe 或 elf 文件,然后从命令行调用,让 .exe 创建一个包含向量的 .txt 并用 python 读取它会更容易吗?

我的观点是,我只需要 C++ 中的一个非常小的函数来对大量数据进行繁重的计算。做到这一点最不痛苦和最短的方法是什么?

编辑: 举个例子。Python 将给 C++ 一个文件名字符串(“foo.txt”),然后它将读取文件的上下文(200,000 行乘 300 列),计算缺失值,然后将每行缺失的数量返回给 Python。这会产生一个包含 200,000 个数字的列表。两人之间如何进行这种交流?

为了完整起见,这就是我仍然想知道如何去做的事情:

  • 将 python 文件名字符串传递给 C++
  • 在 C++ 中接收 python 字符串
  • 完成 在 C++ 中创建向量
  • 将向量返回给 Python
  • 在 Python 中接收向量
4

5 回答 5

6

定义您的入口点extern "C"并使用ctypes.

这是一个让您入门的示例,数据从 Python 传递,C++ 代码对数据进行排序,然后 Python 取回结果:

#include <sys/types.h>
#include <algorithm>

extern "C" {
    void foo(float* arr, size_t len);
}

void foo(float* arr, size_t len)
{
    // if arr is input, convert to C++ array

    // crazy C++ code here
    std::sort(arr, arr+len);

    // if arr is output, convert C++ array to arr
}

将您的代码编译成一个共享对象(Linux 上的 libxxx.so,win 上的 libxxx.dll,osx 上的 libxxx.dylib),然后动态加载它并通过 ctypes 传入/传出数据:

import ctypes
import posix

# Darwin .dylib; Linux .so; Windows .dll; use sys.platform() for runtime detection
libxxx = ctypes.CDLL("./libxxx.so")
libxxx.foo.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_size_t]
libxxx.foo.restype = None

data = ctypes.ARRAY(ctypes.c_float, 100)()

# write someting into data[x]
import random
for i in range(100): data[i] = random.random()
print data[:3], "...", data[-3:]

libxxx.foo(data, len(data))

# read out from data[x]
print data[:3], "...", data[-3:]

很棒的一点ctypes是,它从 2.5 开始就与 Python 捆绑在一起,您不需要任何额外的库。

如果您想使用更高级的东西,请查看cffi.

于 2013-05-29T20:27:02.083 回答
6

这现在可能没有实际意义,我在你的另一个问题上发布了类似的内容,但我已经为 Python 3.3 和 C++ 而不是 Python 2.7 和 C 调整了这个版本。

如果您想取回一个 Python 列表对象,并且由于您正在构建一个可能很长(200,000 个项目)的列表,那么在 C++ 代码中构建 Python 列表可能更有效,而不是构建一个std::vector然后稍后将其转换为 Python 列表。

根据您其他问题中的代码,我建议使用类似的东西......

// foo.cpp
#include <python3.3/Python.h>
#include <fstream>
#include <string>
using namespace std;

extern "C"
{
    PyObject* foo(const char* FILE_NAME)
    {
        string line;
        ifstream myfile(FILE_NAME);
        PyObject* result = PyList_New(0);

        while (getline(myfile, line))
        {
            PyList_Append(result, PyLong_FromLong(1));
        }

        return result;
    }
}

...编译...

$ g++ -fPIC -shared -o foo.so foo.cpp -lpython3.3m

...以及使用示例...

>>> from ctypes import *
>>> foo = CDLL('./foo.so')
>>> foo.foo.restype = py_object
>>> foo.foo(b'foo.cpp')
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

...尽管如果您需要将现有列表转换std::vector为 Python 列表,您可以通过将向量的长度传递给 来预先分配 Python 列表所需的内存PyList_New(),然后使用PyList_SetItem()而不是PyList_Append().

我能想到的唯一其他方法是......

  1. 要在 Python 中预先分配一块 RAM,并让 C++ 函数填写值,就像在 qarma 的回答中一样,但是您必须事先知道要分配多少 RAM。您可以选择一个任意值,但鉴于事先不知道文件中的行数,这个数字可能太大或太小。

  2. 在 C++ 中进行堆分配std::vector,并返回指向第一个元素的指针和元素的数量,但是一旦你完成了它,你就必须编写第二个函数来释放 RAM。

无论哪种方式,您仍然需要将“返回”数组转换为 Python 列表的开销,因此您不妨自己动手。

于 2013-06-03T21:20:36.507 回答
3

你在正确的轨道上。

你有两个可执行文件吗?

最好将其保存到中间文件。锁定文件,从您的 C++ 代码写入文件。从 Python 解锁并读取它。

如果你只想从 python 运行,你总是可以考虑扩展 python:

用 C++ 扩展 Python

还可以选择通过套接字进行,但如果您只想通过列表,这可能有点矫枉过正。

于 2013-05-22T13:35:23.007 回答
3

您可以使用 python 中的 subprocess 模块从您的 c++ exe 文件中读取输出。例如:

c++ 文件:

#include <iostream>
using namespace std;
int main()
{
    int a[]={1,2,3,4};
    cout<<"[";
    for(int i=0; i<3; i++)
        cout<<a[i]<<",";
    cout<<a[3]<<"]";
    return 0;
}

那么你的python脚本将是:

import subprocess
a=subprocess.check_output("c++compiledfile")
l=eval(a)
print l
于 2013-05-22T15:56:20.510 回答
0

您可以在进程间通信中使用字符串:
使用 Python 中的 subprocess.check_output() 函数来检查 C++ 程序的输出,并将文件名作为参数传递:

Python的代码:

import subprocess
from time import clock


ti=clock()
txt_fname='foo.txt'
# pass the filename string to a c++ program, and receive vector in python
output=subprocess.check_output(["./prog", txt_fname])
result = eval(output)   # convert the str_result to a vector/list
tf=clock()

print(result)
print(format(tf-ti, '.3f'))

C++ 代码:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>


int main(int argc, char *argv[])
{
    // receive python string in c++
    char* txt_fname = argv[1];

/*  // read matrix from file in c++
    FILE *f_matrix = fopen(txt_fname, "r");
    // ... [done by the question's author!]
    if (f_matrix!=NULL){
        fclose(f_matrix);
    }*/

    // create the vector in c++
    int n=200000;
    std::vector<int> vect(n);
    // or:  int vect[];
    // ... [done by the question's author!]

    // return the vector to python
    std::cout << "[";
    for (int i=0; i<n; i++)
        std::cout << vect[i] << ", ";
    std::cout << "]";

    return 0;
}

编辑:添加计时器并将“ast.literal_eval()”替换为“eval()”,因为实际上在这种情况下 eval() 更快并且它与 python 3.3 兼容。

于 2013-06-04T03:11:17.777 回答