【发布时间】:2019-10-29 11:53:04
【问题描述】:
- 我尝试从 Python 调用 dll 中的 C++ 函数。
- 我已经成功生成了一个 C++ DLL(*.dll、*.lib 和 *.h),如果我尝试从另一个 C++ 代码调用它的 api 函数,我已经测试了 DLL 库是否可以工作。 (它在 C++ 中工作)
- 但是我正在努力围绕它制作一个 python 包装器。
我已经为此花费了超过 48 小时,因此非常感谢任何形式的帮助/建议/提示/批评!
我在网上阅读了大部分有关相关内容的可用文档,尽管我并不完全理解所有这些内容。通过调整代码的某些部分并再次编译,问题在于链接器无法解析在 dll 头文件 (*.h) 中声明的外部函数。
知道我测试 *.pyx 和 *.h 之间的“连接”是否正常工作可能很有用,因为如果我在 *.pyx 中调用 api 函数时输入的参数较少,错误消息将显示数字的参数与标题中的不一致。所以问题实际上是 *.pyx 和 *.dll 之间的交互,它应该依赖于 *.lib。至少我是这样认为的。
由于我不确定 *pyx 的 *.h 文件是否应该使用“__declspec(dllimport)”或“__declspec(dllexport)”,所以我都尝试了。他们都没有解决这个问题。
换句话说,链接器闻起来好像无法找到 api 函数(符号)的定义在 *.dll 中的位置,因为即使我更改 setup.py 中的库参数(例如,来自库 = [ 'libcds'] to libraries = ['nonexist']),错误信息是一样的,都是“LNK2019: unresolved external symbol __imp_CalcUpfrontCharge_API referenced in function blabla...
所以我怀疑安装文件缺少某些部分,让编译知道它应该查找 *.lib 文件并在 dll 中找到定义。现在它似乎忽略了 *.lib,因为正如我所说,即使我在 extension() 中完全删除了 library 参数,我也会遇到同样的错误。
我也尝试从源代码(不是 dll)构建 pyd,但也失败了。
it is as simple as this (updated CDS_API macro):
libcds.h
#ifdef CDS_EXPORTS
#define CDS_API __declspec(dllexport)
#else
#define CDS_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C"
{
#endif
CDS_API double CalcUpfrontCharge_API(
char *expiries[], double rates[], double couponRate, double
parSpread, double recoveryRate, char *today1, char *valueDate1, char
*effectDate1, char *maturity
);
#ifdef __cplusplus
}
#endif
cdspricer.pyx
# distutils: language = c++
# distutils: libraries = libcds
# distutils: include_dirs = .
# distutils: library_dirs = .
cimport numpy as np
from libc.stdlib cimport malloc, free
from libcpp.vector cimport vector
from libcpp.string cimport string
from libc.string cimport strcpy, strlen
cdef extern from "libcds.h":
double CalcUpfrontCharge_API(
char *expiries[], double rates[], double couponRate, double
parSpread, double recoveryRate, char *today1, char *valueDate1, char
*effectDate1, char *maturity);
def CalcUpfrontCharge(vector[char *] expiries,
vector[double] rates,
double couponRate,
double parSpread,
double recoveryRate,
string today,
string valueDate,
string effectDate,
string maturity
):
some logic...
cdef double res = CalcUpfrontCharge_API(c_expiries, c_rates, couponRate,
parSpread, recoveryRate, today1, valueDate1, effectDate1, maturity1)
return res
setup.py
import setuptools
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np
ext_modules = [
Extension('cdspricer',
['cdspricer.pyx'],
# Note here that the C++ language was specified
# The default language is C
language="c++",
depends = ['N:\Trading\Python\ISDA\libcds.lib'],
extra_link_args = [],
include_dirs=["N:\Trading\Python\ISDA"],
libraries=['libcds'],
library_dirs=["N:\Trading\Python\ISDA"])
]
setup(
name = 'cdspricer',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules,
extra_compile_args=["-O3", "-Wall"],
include_dirs=[np.get_include()]
)
【问题讨论】:
-
您是否链接到导入库?
-
__declspec(dllimport) double CalcUpfrontCharge_API(应该是CDS_API double CalcUpfrontCharge_API( -
你需要在构建你的 dll 时定义 CDS_EXPORTS 否则它不会导出你的符号。并且使用时不要定义。
-
@drescherjm 非常感谢您的回复。你说的对。我怀疑问题是我没有“链接”到导入库,但是如何?我将库依赖项放在 extension() 中,并明确地说 cdef extern from "libcds.h": double CalcUpfrontCharge_API in the *.pyx。我如何“链接到导入库”?另外,我看到扩展中的库 keyworld arg 仅控制哪个 *.lib 提供 link.exe。
-
@drescherjm 是的先生.. 我认为 dll 构建正确(我用于构建 dll 的 *.h 文件定义了 CDS_EXPORTS 并因此遵循您的建议并导出了该函数。我还通过在另一个 C++ 代码中调用导出的函数,成功.. 但是你是对的,如果我在使用时没有定义它,那么值将是“_declspec(dllimport)”
标签: c++ visual-c++ dll compiler-errors cython