Я хотел бы получить список Python, скажем, [1,2,3,4]
из сценария C ++. Я написал скрипт C ++, который возвращает вектор.
Как соединить концы без SWIG / SIP / Cython / и других?
Может быть проще просто скомпилировать C ++ в файл .exe или elf, а затем вызвать из командной строки файл .exe, создать .txt, содержащий вектор, и прочитать его с помощью python?
Суть в том, что мне нужна действительно маленькая функция из C ++, чтобы выполнять тяжелые вычисления с большими данными. Что было бы наименее болезненным и самым коротким способом сделать это?
РЕДАКТИРОВАТЬ:
Чтобы привести пример. Python передаст строку имени файла в C ++ («foo.txt»), который затем прочитает контекст файла (200 000 строк на 300 столбцов), посчитает пропуски и затем вернет Python количество пропущений в строке. Это дает список 200 000 номеров.
Как это общение между ними?
Просто для полноты, это то, что я все еще задаюсь вопросом о том, как это сделать:
Это, вероятно, спорный, и я отвечал что-то похожее на ваш другой вопрос, но я адаптировал эту версию для Python 3.3 и C ++, а не Python 2.7 и C.
Если вы хотите получить обратно объект списка Python, и поскольку вы создаете список, который потенциально может быть очень длинным (200 000 элементов), вероятно, более эффективно построить список Python в коде C ++, а не создавать 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()
.
Единственные другие методы, о которых я могу думать, были бы …
Чтобы предварительно выделить блок оперативной памяти в Python и заставить функцию C ++ заполнять значения, как в ответе qarma, вам нужно заранее знать, сколько оперативной памяти выделить. Вы можете просто выбрать произвольное значение, но, учитывая, что количество строк в файле заранее неизвестно, это число может быть слишком большим или слишком маленьким.
Чтобы куча-выделить std::vector
в C ++ и возвращает указатель на первый элемент и количество элементов, но вам нужно написать вторую функцию, чтобы освободить ОЗУ, как только вы покончили с этим.
В любом случае, у вас все еще есть издержки на преобразование возвращаемого массива в список Python, так что вы можете сделать это самостоятельно.
Определите вашу точку входа 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
}
Скомпилируйте ваш код в общий объект (libxxx.so в linux, libxxx.dll в win, libxxx.dylib в osx), затем загрузите его динамически и передавайте / выводите данные через 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
в том, что он поставляется с Python начиная с 2.5, вам не нужны никакие дополнительные библиотеки.
Если вы хотите использовать что-то более продвинутое, взгляните на cffi
,
Вы на правильном пути.
У тебя есть два исполняемых файла?
Лучше было бы сохранить его в промежуточный файл. Заблокируйте файл, запишите в него код C ++. Разблокируйте и прочитайте его из Python.
Если вы просто хотите запустить из Python, вы всегда можете посмотреть на расширение Python:
Расширение Python с помощью C ++
Есть также возможность сделать это через сокеты, хотя это может быть немного излишним, если все, что вы хотите сделать, это пропустить списки.
Вы можете использовать модуль подпроцесса в python для чтения вывода из вашего exe-файла c ++.
Например:
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
Вы можете работать со строками в межпроцессном взаимодействии:
используйте функцию subprocess.check_output () в Python, чтобы проверить вывод программы C ++ и передать имя файла в качестве аргумента:
Код Python:
import subprocess
from time import clockti=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.