尝试在Windows中使用C ++ DLL库编译Cython代码时出现链接错误[LNK2019]

时间:2019-06-14 20:55:41

标签: c++ visual-c++ dll compiler-errors cython

  1. 我试图从python调用dll中的C ++函数。
  2. 我已经成功生成了一个C ++ DLL(*。dll,*。lib和* .h),并且测试了如果我尝试仅从另一个C ++代码调用其api函数,则DLL库可以工作。 (它在C ++中运行)
  3. 但是我正在努力围绕它制作一个python包装器。

我在此上花费了48多个小时,因此非常感谢任何帮助/建议/提示/批评家!

尽管我并不完全了解所有相关内容,但我还是在线阅读了大多数相关文档。通过调整代码的某些部分并再次编译,闻起来像是问题在于链接器无法解析dll头文件(* .h)中声明的外部函数。

知道我测试* .pyx和* .h之间的“连接”是否有效可能很有用,因为如果在* .pyx中调用api函数时输入的参数较少,则错误消息将显示数字的参数与标题中的参数不一致。因此,问题实际上是* .pyx和* .dll之间的相互作用,该相互作用应取决于* .lib。至少我是这样想的。

由于我不确定* pyx的* .h文件是否应使用“ __declspec(dllimport)”或“ __declspec(dllexport)”,因此我尝试了两者。他们俩都没有解决问题。

换句话说,闻起来好像链接器无法在* .dll中找到api函数(符号)的定义所在,因为即使我更改了setup.py中的库参数(例如,来自库= [ 'libcds']到库= ['nonexist']),错误消息相同,即“ LNK2019:函数blabla中引用的未解析的外部符号__imp_CalcUpfrontCharge_API ...

因此,我怀疑安装文件缺少某些部分,让编译知道它应该查找* .lib文件并在dll中找到定义。现在看来,它正在忽略* .lib,因为正如我所说,即使我完全在extension()中删除了libraries参数,我也遇到了同样的错误。

我还尝试从源代码(不是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()] 
)

2 个答案:

答案 0 :(得分:0)

好的,我回来回答自己的问题。

在苦苦挣扎了36个小时之后,我终于使它可以在Windows7系统上运行。 由于使用C / C ++ DLL的Cython编译需要太多的细节和技巧,因此其中任何一项都是至关重要的,以至于即使缺少一个看似微不足道的步骤也可能导致编译/链接过程完全失败,我将着重介绍所有容易出错的陷阱我遇到的问题和解决方案:

  1. 您的设置扩展程序必须遵循以下形状:
    ext_modules = [
        Extension('cdspricer',
              ['cdspricer.pyx'],
              # Note here that the C++ language was specified
              # The default language is C
              language="c++",
              extra_link_args = [],
              include_dirs=["N:\Trading\Python\ISDA"],
              libraries=['cdslib', 'msvcrt', 'msvcmrt'],
              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()]  # This gets all the required Numpy core files
    )
  1. 重要的是,如果您总是遇到错误:LNK2001 / LNK2019,请检查以确保在构建DLL时,不仅标头文件在导出的函数声明的前面具有__declspec(dllexport),而且源文件中的函数定义也应遵循__declspec(dllexport)!原来,我在源文件中缺少前缀,因此该dll在其他C ++项目中有效,但在Cython中不起作用!
#ifdef CDSLIB_EXPORTS
#define CDSLIB_API __declspec(dllexport)
#else
#define CDSLIB_API __declspec(dllimport)
#endif

    extern "C"
    {
        CDSLIB_API double CalcUpfrontCharge_API(
            char *expiries[], double rates[], double couponRate, double parSpread, double 
    recoveryRate, char *today1, char *valueDate1, char *effectDate1, char *maturity
        );
        CDSLIB_API double Test(
            double a
        );
    }

在源代码中,您具有:

    CDSLIB_API double Test(double a)
    {
        double b;
        b = a + 1;
        return b;
    }

Test函数将起作用,而CalcUpfrontCharge_API将不起作用。

  1. 请记住,将纯C代码函数的声明包装为extern“ C”
  2. 非常重要的一点是,如果您的dll是在x86平台上构建的,它将永远无法与64位python一起使用!最好的解决方案是使用Visual Studio(我使用VC2015),您可以在其中将解决方案平台从x86切换到x64。

答案 1 :(得分:0)

因此,我只是使用将创建插件外壳的插件SDK向导遇到了这个问题。它不会仅使用shell进行构建,并且会出现此错误。特定于这种类型的设置,但对我而言,解决方法是我收到此警告:

warning C4335: Mac file format detected: please convert the source file to either DOS or UNIX format

将文件保存为正确的格式后,未解决的外部符号错误消失了,我可以构建解决方案。

虽然我知道这可能无法解决您的情况,但我希望这可以帮助某人寻找答案。