我在C++中有一个函数,它接受整数集的列表,并在集之间有公共元素的地方合并集。代码从这里开始:基于公共元素组合成对的整数。
我的C++文件名为merge_sets.cpp,包含以下代码:
#include <algorithm>
#include <set>
#include <list>
#include <iostream>
std::list<std::set<int>> Merge(std::list<std::set<int>> values){
for( std::list<std::set<int>>::iterator iter = values.begin(); iter != values.end(); ++iter)
for(std::list<std::set<int>>::iterator niter(iter); ++niter != values.end();)
if(std::find_first_of(iter->begin(), iter->end(), niter->begin(), niter->end()) != iter->end())
{
iter->insert(niter->begin(), niter->end());
values.erase(niter);
niter = iter;
}
return values;
}
extern "C" {
std::list<std::set<int>> merge(std::list<std::set<int>> test){
return Merge(test);
}
}
我想创建一个.so共享库文件,并使用cdll将其加载到python中。LoadLibrary函数。我用以下命令创建.so文件:
g++ -c -fPIC merge_sets.cpp -Wextra -Wall -o merge_sets.o
g++ -shared -Wl,-soname,merge_sets.so -o merge_sets.so merge_sets.o
这成功地创建了merge_sets.so共享库。然后我尝试在python中加载库,如下所示:
from ctypes import cdll
lib = cdll.LoadLibrary("./merge_sets.so")
当我尝试运行.py脚本时,我会得到以下错误:
Traceback (most recent call last):
File "c:Userstjs_1DocumentsGit Repossingle-customer-viewtesting_python_cpp_bindings.py", line 3, in <module>
lib = cdll.LoadLibrary("./merge_sets.so")
File "C:Userstjs_1anaconda3envsSCV_envlibctypes__init__.py", line 452, in LoadLibrary
return self._dlltype(name)
File "C:Userstjs_1anaconda3envsSCV_envlibctypes__init__.py", line 374, in __init__
self._handle = _dlopen(self._name, mode)
FileNotFoundError: Could not find module 'C:Userstjs_1DocumentsGit Repossingle-customer-viewmerge_sets.so' (or one of its dependencies). Try using the full path with constructor syntax.
我怀疑这是C++脚本中依赖项的问题,因为当我将merge_sets函数转换为一个接受和integer并返回相同整数的toy函数,并且从脚本顶部删除所有#include语句时,代码运行良好。
编辑:
如果我将merge_sets更改为:
int Merge(int test){
return test;
}
extern "C" {
int merge(int test){
return Merge(test);
}
}
并重新编译到merge_sets.so,然后使用相同的Python脚本加载,就完全没有问题了。
作为测试,如果我将merge_sets.cpp更改为:
#include <algorithm>
#include <set>
#include <list>
int Merge(int test){
std::list<std::set<int>> values = {{1, 2}, {2, 4}, {8, 3}};
for( std::list<std::set<int>>::iterator iter = values.begin(); iter != values.end(); ++iter)
for(std::list<std::set<int>>::iterator niter(iter); ++niter != values.end();)
if(std::find_first_of(iter->begin(), iter->end(), niter->begin(), niter->end()) != iter->end())
{
iter->insert(niter->begin(), niter->end());
values.erase(niter);
niter = iter;
}
return test;
}
extern "C" {
int merge(int test){
return Merge(test);
}
}
我犯了和以前一样的错误。
您可以使用python c api。这是一个带有单个调用的简单扩展的示例。
// theExtension.cpp
//
/////////////////////////////////////////////////////////////////
//
// Primary module for extension.
//
/////////////////////////////////////////////////////////////////
// Numpy uses a variable PyArray_API to define API methods; this is initialised by calling import_array.
// By default this initialises to a static variable, so is only available in the current module.
// Defining PY_ARRAY_UNIQUE_SYMBOL indicates that the variable should be not be declared
// as static, so it can be linked to from other .cpp files. Other .cpp files must define NO_IMPORT_ARRAY
// which means PyArray_API is declared as extern, hence it is not defined multiple times.
//
#define PY_ARRAY_UNIQUE_SYMBOL VectorAdd_ARRAY_API
#define NPY_NO_DEPRECATED_API NPY_1_17_API_VERSION
#include <arrayobject.h>
/////////////////////////////////////////////////////////////////
const char* g_szBasicAdd = ""
"basic_add(output, a, b) n"
" n"
"Adds two numpy arrays and writes result to output `output = a + b`n"
" n"
"Returns: n"
"- None n";
PyObject* basic_add(PyObject*, PyObject* args)
{
PyObject* pPyA;
PyObject* pPyB;
PyObject* pPyO;
if(!PyArg_ParseTuple(args, "OOO", &pPyO, &pPyA, &pPyB)) // object references are borrowed so do not decrement
{
PyErr_SetString(PyExc_ValueError, "Failed to parse input parameters.");
return nullptr;
}
PyArrayObject* pPyArrayA = reinterpret_cast<PyArrayObject*>(PyArray_FROM_OTF(pPyA, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY));
if(pPyArrayA == nullptr)
{
PyErr_SetString(PyExc_ValueError, "Failed to cast a.");
return nullptr;
}
size_t iSizeA = static_cast<size_t>(PyArray_SIZE(pPyArrayA));
const double* pcdA = static_cast<const double*>(PyArray_DATA(pPyArrayA));
PyArrayObject* pPyArrayB = reinterpret_cast<PyArrayObject*>(PyArray_FROM_OTF(pPyB, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY));
if(pPyArrayB == nullptr)
{
PyErr_SetString(PyExc_ValueError, "Failed to cast b.");
return nullptr;
}
size_t iSizeB = static_cast<size_t>(PyArray_SIZE(pPyArrayB));
const double* pcdB = static_cast<const double*>(PyArray_DATA(pPyArrayB));
PyArrayObject* pPyArrayO = reinterpret_cast<PyArrayObject*>(PyArray_FROM_OTF(pPyO, NPY_DOUBLE, NPY_ARRAY_OUT_ARRAY));
if(pPyArrayO == nullptr)
{
PyErr_SetString(PyExc_ValueError, "Failed to cast o.");
return nullptr;
}
size_t iSizeO = static_cast<size_t>(PyArray_SIZE(pPyArrayO));
double* pdO = static_cast<double*>(PyArray_DATA(pPyArrayO));
if(iSizeO != iSizeA || iSizeO != iSizeB)
{
PyErr_SetString(PyExc_ValueError, "Failed input arrays different sizes.");
return nullptr;
}
for(size_t i = 0; i < iSizeO; ++i)
{
pdO[i] = pcdA[i] + pcdB[i];
}
Py_INCREF(Py_None);
return Py_None;
}
/////////////////////////////////////////////////////////////////
static PyMethodDef VectorAdd_Methods[] = {
// The first property is the name exposed to Python, the second is the C++
// function name that contains the implementation.
{ "basic_add", static_cast<PyCFunction>(basic_add), METH_VARARGS, g_szBasicAdd},
// Terminate the array with an object containing nulls.
{ nullptr, nullptr, 0, nullptr }
};
/////////////////////////////////////////////////////////////////
static PyModuleDef VectorAdd_Module = {
PyModuleDef_HEAD_INIT,
"VectorAdd", // Module name to use with Python import statements
"C++ module demonstrating C Api", // Module description
0, //
VectorAdd_Methods // Structure that defines the methods of the module
};
/////////////////////////////////////////////////////////////////
static bool m_bInitNumpyAPI = false;
void* _initNumpyAPI()
{
if(!m_bInitNumpyAPI)
{
import_array();
m_bInitNumpyAPI = true;
}
return NULL;
}
/////////////////////////////////////////////////////////////////
PyMODINIT_FUNC PyInit_VectorAdd()
{
_initNumpyAPI();
return PyModule_Create(&VectorAdd_Module);
}
////////////////////////////////////////////////////////////////
在Windows中,您可以在Visual Studio中构建DLL。您需要将$(gc-3-10)include;$(gc-3-10)Libsite-packagesnumpycoreincludenumpy
添加到Include Directories
中,其中$(gc-3-10)
是您的python env的目录;然后将$(gc-3-10)Libsite-packagesnumpycorelib;$(gc-3-10)libs;
设置为您的Library Directories
,然后使用设置为Dynamic Library
的Configuration Type
和设置为.pyd
的Target File Extension
进行构建;我从来没有试过在Windows中使用gcc构建python扩展。
在linux上,你可以用gcc构建一个共享的.so库,比如:
#!/bin/bash
# file: build.sh
(
cd "$(dirname "$0")"
g++-11 -fPIC
-I ~/anaconda3/envs/py38/include/python3.8
-I ~/anaconda3/envs/py38/lib/python3.8/site-packages/numpy/core/include/numpy
-L"$(echo ~/anaconda3/envs/py38/lib)"
-shared
-o ../Lib/MyLib.so
./theExtension.cpp
-lpython3.8
-O3
-std=c++20
-lz
)
一旦你构建了这个,将.so/.pyd目录添加到PYTHONPATH中,然后你就可以在python中使用它,比如:
import numpy as np
from VectorAdd import basic_add
a = np.array([1., 2., 3., 4.])
b = np.array([2., 4., 6, 8.])
o = np.zeros(4, dtype=np.float64)
basic_add(o, a, b)