ошибка компоновки [LNK2019] появилась при попытке компилировать код Cython с библиотеками DLL C ++ в Windows - PullRequest
1 голос
/ 14 июня 2019
  1. Я пытался вызывать функции C ++ в dll из Python.
  2. Я успешно создал C ++ DLL (* .dll, * .lib и * .h), и у меня есть тест, что библиотека DLL работает, если я пытаюсь вызывать ее функции API только из другого кода C ++.(это работает в C ++)
  3. Однако я изо всех сил пытаюсь сделать вокруг него оболочку Python.

Я потратил на это более 48 часов, поэтому я очень ценю любую помощь / совет / подсказку / критику !!

Я прочитал большинство доступных документов о связанных материалахонлайн, хотя я не полностью понимаю их все.Изменяя некоторую часть кода и снова компилируя, пахнет тем, что проблема в том, что компоновщик не может разрешить внешние функции, объявленные в заголовочном файле dll (* .h).

Вероятно, полезно знать, что я проверяю, работает ли "соединение" между * .pyx и * .h, потому что, если при вызове функции api в * .pyx я добавлю меньше аргументов, появится сообщение об ошибке.скажем, количество параметров не совпадает с указанным в заголовке.так что проблема на самом деле заключается во взаимодействии * .pyx и * .dll, которое должно зависеть от * .lib.По крайней мере, так я думаю.

Поскольку я не уверен, должен ли файл * .h для * pyx использовать «__declspec (dllimport)» или «__declspec (dllexport)», я попробовал оба варианта.Ни один из них не решил проблему.

Другими словами, он пахнет так, будто компоновщик не может найти, где находится определение функции API (символа) в * .dll, потому что даже если я изменю аргументы библиотекв файле setup.py (например, из library = ['libcds'] to library = ['nonexist']) ошибка msg та же, что и "LNK2019: неразрешенный внешний символ __imp_CalcUpfrontCharge_API, на который ссылается функция blabla ...

Так что я подозреваю, что в установочном файле отсутствует какая-то часть, чтобы компиляция знала, что должна найти файл * .lib и найти определение в dll. Сейчас кажется, что он игнорирует * .lib, потому что, как ясказал, что даже если я полностью вычеркну аргумент библиотеки в extension (), я получу ту же ошибку.

Я также пытался собрать pyd из исходного кода (не dll), но также потерпел неудачу.

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 голосов
/ 19 июня 2019

Так что я просто столкнулся с этой проблемой, используя мастер плагинов SDK, который создаст оболочку плагинов.Он не собирался только с оболочкой и получал эту ошибку.Это было бы специфично для этого типа установки, но исправление для меня состояло в том, что я получал это предупреждение:

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

После сохранения файлов в правильном формате, неразрешенная внешняя ошибка символа исчезла, и я могпостроить решение.

Хотя я знаю, что это может не исправить вашу ситуацию, я надеюсь, что это может помочь кому-то искать ответ.

0 голосов
/ 15 июня 2019

Хорошо, я вернулся, чтобы ответить на свой вопрос.

После 36 часов борьбы со сборкой я наконец-то заставил ее работать в моей системе Windows7. Поскольку компиляция Cython с C / C ++ DLL влечет за собой так много деталей и уловок, любой из которых настолько критичен, что даже пропущенный, казалось бы, тривиальный шаг может привести к полному провалу процесса компиляции / компоновки, я выделю все подверженные ошибкам ловушки что я столкнулся и решения:

  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;
    }

Функция тестирования будет работать, а CalcUpfrontCharge_API - нет.

  1. Не забудьте обернуть объявления функций в чистом коде C с помощью extern "C"
  2. Очень важно, если ваша dll построена на платформе x86, она никогда не будет работать с 64-битным питоном! Лучшее решение - использовать Visual Studio (я использовал VC2015), где вы можете переключить платформу решения с x86 на x64.
...