带有 std::string 成员的导出 C++ 类的 Cython 包装器无法返回对象

问题描述 投票:0回答:1

我正在尝试学习如何在 Cython 中包装 C++ 库类(其构造函数以 std::string 作为参数)来为我的库构建 python 接口。但是生成的模块无法返回我尝试包装的类的对象,即使它没有返回任何错误或异常。我在下面提供了一个最小的(但不幸的是仍然很长)示例:

我的玩具项目的目录结构如下(下面提到的所有路径都是相对于所示的PBTOY2项目根目录):

C:\USERS\<MyName>\PROJECTS\PBTOY2
├───Examples
└───MyLib
    ├───include
    │   └───MyLib
    ├───python
    │   └───src
    └───src
        └───MyLib

我的玩具库在

MyLib/include/MyLib/mylib.h
中定义如下:

#pragma once
#include <iostream>
#include <string>
#include <MyLib/exports.h>

EXPIMP_TEMPLATE template class MYLIB_API std::basic_string<char,std::char_traits<char>,std::allocator<char>>;

class MYLIB_API Person {

public:
     Person(std::string name, int id, std::string email);
     Person(int id);
    ~Person();

    const std::string getName()  const;
    const std::string getEmail() const;
    const int         getID()    const;
    
    const bool        isValid()  const;

private:

    std::string name{};
    int         id{};
    std::string email{};

};

其中

exports.h
位于
MyLib/include
并包含:

#pragma once
#ifdef MYLIB_EXPORTS
#  define MYLIB_API __declspec(dllexport)
#  define EXPIMP_TEMPLATE
#else
#  define MYLIB_API __declspec(dllimport)
#  define EXPIMP_TEMPLATE extern
#endif

该库在

MyLib/src/MyLib/mylib.cpp
中实现如下:

#include <MyLib/mylib.h>

Person::Person(std::string name, int id, std::string email) : name{ name }, id{ id }, email{ email } {
}

Person::Person(int id) : id{ id } {
}

Person::~Person() {}

const std::string Person::getName() const {
    return name;
}
const std::string Person::getEmail() const {
    return email;
}
const int Person::getID() const {
    return id;
}
const bool Person::isValid() const {
    if (name.empty()) {
        return false;
    }
    return true;
}

我使用从

example1.cpp
中的源代码构建的可执行文件来测试该库(在
Examples
中找到,其中包含以下内容:

#include <iostream>
#include <MyLib/mylib.h>

int main (int argc, char *argv[]) {

    std::string name{ "Homer" };
    int         id {1};
    std::string email{ "[email protected]"};

    Person p = Person(name, 1, email);
    std::cout << "Hello, " << p.getName() << "! You are number " << p.getID() << "! Can I contact you at " << p.getEmail() << "?" << std::endl;

    return 0;

}

所有这些都可以正常编译和运行(在 Windows 10 环境中使用 CMake)。库编译为

mylib.dll
,可执行文件编译为
toy-example.exe

同时,在

MyLib/python/src
中,我创建了文件
MyLib.pxd
,其中包含以下内容:

# distutils: language = c++

from libcpp.string cimport string

cdef extern from "mylib.h":

    cdef cppclass Person:
       Person(string, int, string) except +
       string getName()
       string getEmail()
       int getID()

以及文件

MyLib.pyx
其中包含:

# cython: c_string_type=unicode, c_string_encoding=utf8

from MyLib cimport Person
    
cdef class PyPerson:
    cdef Person* c_person_ptr

    def __cinit__(self, str name, int id, str email):
        print("creating the pointer for person with name ", name)
        self.c_person_ptr = new Person(name, id, email)
        print("created the pointer")

    def get_name(self):
        return self.c_person_ptr.getName().decode()

    def get_id(self):
        return self.c_person_ptr.getID()

    def get_email(self):
        return self.c_person_ptr.getEmail()
    
    def __dealloc__(self):
        print("deallocating pointer")
        del self.c_person_ptr

我在

setup.py
中有一个
MyLib/python
文件,其中包含:

from setuptools import setup, Extension
from Cython.Build import cythonize

import os

ext = [Extension(name        = "*",
                 sources     = [os.path.join(os.path.dirname(__file__), "src", "*.pyx")],
                language     = "c++",
                library_dirs = ["../../build/MyLib/Debug"],
                libraries    = ['MyLib',])
]

setup( name                  = "MyLib",
       ext_modules           = cythonize(ext, language_level = "3"), 
       include_dirs          = ['../include/MyLib', '../include'], 
     )

最后,一个用于测试库的 python 脚本

import os

dllPath  = r'C:\Users\<MyHomeDirectory>\projects\PBToy2\build\install\bin'

os.add_dll_directory(dllPath)

if os.path.exists(dllPath):
    import MyLib

    print("I'm really here")
    myPerson = MyLib.PyPerson("Homer", 0, "[email protected]")
    
    print( "my ID is ", myPerson.get_id())

else:
    print("sorry, couldn't find the module")

(我在上面编辑了我的主目录的实际名称)

我正在使用命令

MyLib
MyLib/python
中构建
python setup.py build_ext --inplace
扩展。

我确实收到一个关于该类需要 dll 接口的警告

std::_Compressed_pair<std::allocator<char>,std::_String_val<std::_Simple_types<_Elem>>,true>
但是当我尝试在
mylib.h
中包含为此的指令时,Visual Studio 无法识别该类型。 (细心的读者会注意到,我确实在
MyLib.h
中有一个 Visual Studio 能够识别的另一种类型的指令。我把它放在那里是为了解释我收到的另一个警告)。

python 模块构建成功,并且

useMylib.py
确实运行,但永远不会从
__cinit__
返回并带有指向
Person
的指针,并且对
myPerson.get_id()
的调用永远没有机会执行。在到达
MyLib.pyx
中的打印语句后,它只是默默地停止,其中我打印了“为具有姓名的人创建指针...”,这表明指针永远不会从该打印语句下方的调用中返回。

我已经能够使用在库外部定义的类来执行此类操作,但我需要能够在编译为 dll 的库内执行此操作,如此处所示的情况。

我真的很困惑,我在这里或其他任何地方都没有看到任何可以称之为“重复”的问题。如果我忽略了一些事情,我很抱歉。提前感谢您仔细研究这个问题!

c++ dll cython
1个回答
0
投票

好吧,我解决了我自己的问题(某种程度上)。通常会发生这种情况,但这次不是在发布我的问题之前。我正在使用调试配置构建我的 C++ 项目。我一时兴起决定尝试发布配置,然后voila。问题就消失了。我希望这对像我这样尝试做类似事情的人有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.