cython 重用包装 C++ class
cython reuse wrapped C++ class
我想用 cython 包装一些 C++ class,但我的问题是我必须在另一个 class 中使用一些 class。我还没有找到其他人遇到同样的问题,甚至没有找到类似的问题,如果已经有人问过,我很抱歉...我做了一个已经很长的简约示例...
所以我有以下文件结构,其中 BuildAll.sh 运行 通过文件夹,如果它有一个 BuildAll.sh 文件,它会更深,如果一个文件夹有一个 setup.py 文件,它将构建它(setup.py 文件将查找每个 *.pyx 文件并构建它们)
现在出于虚拟目的 classAC 是用 C++ 编写的 ClassA,它包含一个 int 作为成员。
同样在 B 文件夹中,但是 classBC 持有 classAC 的一个实例作为成员,我认为这是问题所在,因为 classBC 不知道 classAC 定义。
.
├── BuildAll.sh
├── main.py
└── src
├── A
│ ├── classAC.cpp
│ ├── classAC.hpp
│ ├── classAC.pxd
│ ├── classA.pyx
│ ├── __init__.py
│ └── setup.py
├── B
│ ├── classBC.cpp
│ ├── classBC.hpp
│ ├── classBC.pxd
│ ├── classB.pyx
│ ├── __init__.py
│ └── setup.py
├── BuildAll.sh
└── __init__.py
BuildAll.sh:
#!/bin/bash
# Go into every directory
for D in */
do
cd $D
# If dir contains a BuildAll.sh script, run it.
if test -f "BuildAll.sh"; then
./BuildAll.sh
fi
# If dir contains a setup.py file, then build it.
# setup.py will look for every .pyx extension and build it automatically.
if test -f "setup.py"; then
python3 setup.py build_ext --inplace
fi
cd ..
done
setup.py:
from setuptools import Extension, setup
from Cython.Build import cythonize
import glob
PYXFILES = glob.glob("*.pyx")
EXTNAMES = [i[:-4] for i in PYXFILES]
ext_list = []
for i in range(len(PYXFILES)):
ext_list.append(
Extension(
EXTNAMES[i],
[PYXFILES[i]],
extra_compile_args=["-O3"]
)
)
setup(
ext_modules = cythonize(
ext_list,
language_level = 3,
build_dir = 'build',
annotate = True
)
)
classAC.cpp:
#include "classAC.hpp"
classAC::classAC()
: data_(0)
{}
int classAC::data()
{
return data_;
}
classAC.hpp:
#ifndef CLASSAC_H
#define CLASSAC_H
class classAC
{
private:
int data_;
public:
// Constructors
// Null construct
classAC();
// Destructor
~classAC() = default;
int data();
};
#endif // CLASSAC_H
classAC.pxd:
cdef extern from "classAC.cpp":
pass
cdef extern from "classAC.hpp":
cdef cppclass classAC:
classAC()
int data()
classA.pyx:
# distutils: language = c++
from classAC cimport *
cdef class ClassA:
cdef classAC COBJ
def __cinit__(self):
pass
def __init__(self):
self.COBJ = classAC()
def getAdata(self):
return self.COBJ.data()
classBC.cpp:
#include "classBC.hpp"
classBC::classBC()
: aobj_(classAC())
{}
classBC.hpp:
#ifndef CLASSBC_H
#define CLASSBC_H
#include "classAC.hpp"
class classBC
{
/* Base class for an fvMesh */
private:
classAC aobj_;
public:
// Constructors
classBC();
// Destructor
~classBC() = default;
};
#endif // CLASSBC_H
classBC.pxd:
cdef extern from "classBC.cpp":
pass
cdef extern from "classBC.hpp":
cdef cppclass classBC:
classBC()
classB.pyx:
# distutils: language = c++
# distutils: include_dirs = ../A
from classBC cimport *
cdef class ClassB:
"""
"""
cdef classBC COBJ
def __cinit__(self):
pass
def __init__(self):
self.COBJ = classBC()
如果我尝试 运行 我的 main.py:
#!/usr/bin/python3
from src.A.classA import ClassA
from src.B.classB import ClassB
a = ClassA()
print(a.getAdata())
b = ClassB()
我收到以下错误:
Traceback (most recent call last):
File "./main.py", line 4, in <module>
from src.B.classB import ClassB
ImportError: <pathToThisFolder>/src/B/classB.cpython-38-x86_64-linux-gnu.so: undefined symbol: _ZN7classACC1Ev
我已经尝试 link 将 classA.longname.so 文件保存到 classBC 但没有帮助。
甚至有可能实现此功能吗?如果是,怎么做?
我想把所有东西分开,而不是有一个巨大的模块。
我的目标是保持每个 setup.py 文件原样,并且只在 pyx 文件中使用 #distutils 添加特定的扩展选项。
谢谢你的帮助,请告诉我这个结构是不是真的很糟糕...
您通常会在 setup.py 中使用“来源”选项,或者在您的 .pyx 文件中使用 # distutils: sources = filename.cpp, another_filename.cpp
指令。这记录在 https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#configuring-the-c-build 中。您这样做而不是将 cpp 文件逐字包含到您的 pxd 文件中!
因此您的 classB.pyx
包含行
# distutils: sources = classBC.cpp classAC.cpp
因此它们都将被编译并 linked 到扩展模块中。
distutils 指令方法可能比将它放在 setup.py 中更好,因为在使用它们的地方记录了依赖项。
另一种方法(根据评论可能对您更好)是 link 将所有 C++ 代码放在一个动态库中,然后 link 每个单独的 Cython 模块使用该库(使用 distutils: libraries = ...
。同样,您只是告诉您在 Cython 代码中包含头文件(而不是 .cpp 文件)。
我想用 cython 包装一些 C++ class,但我的问题是我必须在另一个 class 中使用一些 class。我还没有找到其他人遇到同样的问题,甚至没有找到类似的问题,如果已经有人问过,我很抱歉...我做了一个已经很长的简约示例...
所以我有以下文件结构,其中 BuildAll.sh 运行 通过文件夹,如果它有一个 BuildAll.sh 文件,它会更深,如果一个文件夹有一个 setup.py 文件,它将构建它(setup.py 文件将查找每个 *.pyx 文件并构建它们) 现在出于虚拟目的 classAC 是用 C++ 编写的 ClassA,它包含一个 int 作为成员。 同样在 B 文件夹中,但是 classBC 持有 classAC 的一个实例作为成员,我认为这是问题所在,因为 classBC 不知道 classAC 定义。
.
├── BuildAll.sh
├── main.py
└── src
├── A
│ ├── classAC.cpp
│ ├── classAC.hpp
│ ├── classAC.pxd
│ ├── classA.pyx
│ ├── __init__.py
│ └── setup.py
├── B
│ ├── classBC.cpp
│ ├── classBC.hpp
│ ├── classBC.pxd
│ ├── classB.pyx
│ ├── __init__.py
│ └── setup.py
├── BuildAll.sh
└── __init__.py
BuildAll.sh:
#!/bin/bash
# Go into every directory
for D in */
do
cd $D
# If dir contains a BuildAll.sh script, run it.
if test -f "BuildAll.sh"; then
./BuildAll.sh
fi
# If dir contains a setup.py file, then build it.
# setup.py will look for every .pyx extension and build it automatically.
if test -f "setup.py"; then
python3 setup.py build_ext --inplace
fi
cd ..
done
setup.py:
from setuptools import Extension, setup
from Cython.Build import cythonize
import glob
PYXFILES = glob.glob("*.pyx")
EXTNAMES = [i[:-4] for i in PYXFILES]
ext_list = []
for i in range(len(PYXFILES)):
ext_list.append(
Extension(
EXTNAMES[i],
[PYXFILES[i]],
extra_compile_args=["-O3"]
)
)
setup(
ext_modules = cythonize(
ext_list,
language_level = 3,
build_dir = 'build',
annotate = True
)
)
classAC.cpp:
#include "classAC.hpp"
classAC::classAC()
: data_(0)
{}
int classAC::data()
{
return data_;
}
classAC.hpp:
#ifndef CLASSAC_H
#define CLASSAC_H
class classAC
{
private:
int data_;
public:
// Constructors
// Null construct
classAC();
// Destructor
~classAC() = default;
int data();
};
#endif // CLASSAC_H
classAC.pxd:
cdef extern from "classAC.cpp":
pass
cdef extern from "classAC.hpp":
cdef cppclass classAC:
classAC()
int data()
classA.pyx:
# distutils: language = c++
from classAC cimport *
cdef class ClassA:
cdef classAC COBJ
def __cinit__(self):
pass
def __init__(self):
self.COBJ = classAC()
def getAdata(self):
return self.COBJ.data()
classBC.cpp:
#include "classBC.hpp"
classBC::classBC()
: aobj_(classAC())
{}
classBC.hpp:
#ifndef CLASSBC_H
#define CLASSBC_H
#include "classAC.hpp"
class classBC
{
/* Base class for an fvMesh */
private:
classAC aobj_;
public:
// Constructors
classBC();
// Destructor
~classBC() = default;
};
#endif // CLASSBC_H
classBC.pxd:
cdef extern from "classBC.cpp":
pass
cdef extern from "classBC.hpp":
cdef cppclass classBC:
classBC()
classB.pyx:
# distutils: language = c++
# distutils: include_dirs = ../A
from classBC cimport *
cdef class ClassB:
"""
"""
cdef classBC COBJ
def __cinit__(self):
pass
def __init__(self):
self.COBJ = classBC()
如果我尝试 运行 我的 main.py:
#!/usr/bin/python3
from src.A.classA import ClassA
from src.B.classB import ClassB
a = ClassA()
print(a.getAdata())
b = ClassB()
我收到以下错误:
Traceback (most recent call last):
File "./main.py", line 4, in <module>
from src.B.classB import ClassB
ImportError: <pathToThisFolder>/src/B/classB.cpython-38-x86_64-linux-gnu.so: undefined symbol: _ZN7classACC1Ev
我已经尝试 link 将 classA.longname.so 文件保存到 classBC 但没有帮助。
甚至有可能实现此功能吗?如果是,怎么做?
我想把所有东西分开,而不是有一个巨大的模块。
我的目标是保持每个 setup.py 文件原样,并且只在 pyx 文件中使用 #distutils 添加特定的扩展选项。
谢谢你的帮助,请告诉我这个结构是不是真的很糟糕...
您通常会在 setup.py 中使用“来源”选项,或者在您的 .pyx 文件中使用 # distutils: sources = filename.cpp, another_filename.cpp
指令。这记录在 https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#configuring-the-c-build 中。您这样做而不是将 cpp 文件逐字包含到您的 pxd 文件中!
因此您的 classB.pyx
包含行
# distutils: sources = classBC.cpp classAC.cpp
因此它们都将被编译并 linked 到扩展模块中。
distutils 指令方法可能比将它放在 setup.py 中更好,因为在使用它们的地方记录了依赖项。
另一种方法(根据评论可能对您更好)是 link 将所有 C++ 代码放在一个动态库中,然后 link 每个单独的 Cython 模块使用该库(使用 distutils: libraries = ...
。同样,您只是告诉您在 Cython 代码中包含头文件(而不是 .cpp 文件)。