使用SWIG包装一个调用另一个对象成员函数的C ++类

时间:2014-01-18 01:33:36

标签: python c++ python-2.7 swig

我正在使用swig将包装器写入c ++类以与python一起使用。

当我尝试from CSMPy import *CSMPy是我的模块)时,我收到此消息:

ImportError: dlopen(/Users/MUL_mac2/anaconda/lib/python2.7/site-packages/_CSMPy.so, 2): Symbol not found: __ZN4csmp4VSetILm2EE6ResizeERKSt5dequeIiSaIiEERKS2_ImSaImEESA_m
  Referenced from: /Users/MUL_mac2/anaconda/lib/python2.7/site-packages/_CSMPy.so
  Expected in: dynamic lookup

一点背景知识:

我有一个接口文件,其中包含一个包含我的包装类的头文件:

此类将对象作为私有成员。

然后我想将一些std::deque<int>类型的对象传递给成员函数 像这样的对象:this->object.Function(int_deque_a,int_deque_b)其中object是我使用swig包装的类的成员。

当我评论上述内容时,一切都像魅力一样。 我传递的所有容器都是有效的数据类型,可以传递给这个对象的成员函数并包含正确数量的条目。

所有内容都会编译,只有在导入模块时才会发生这种情况。

我在这里缺少什么?

我正在使用distutils使用python setup.py install

进行编译

setup.py:

CSMPy_module = Extension('_CSMPy',
                                  include_dirs = [Bunch of include directories here],
                                  library_dirs = ['MyLibraryPath'],
                                  libraries = ['MyLibrary'],
                                  sources=['CSMPy_wrap.cxx', 'WrapperClass.cpp'],
                                  )
setup (name = 'CSMPy',
   version = '0.1',
   author = "My name",
   description = """Simple Test""",
   ext_modules = [CSMPy_module],
   py_modules = ["CSMPy"],
   )

MyLibrary是一个静态库。

编辑1: 我正在向您提供我可以向所有人展示的代码版本

Setup.h

#include <iostream>
#include <vector>
#include <deque>

#include "VSet.h"


class Setup {
public:
  Setup();
  ~Setup();

  void InitializeSetup();

private:

  std::deque<size_t> npes;
  std::deque<size_t> epes;

  std::deque<std::vector<size_t> > eni; //plist
  std::deque<std::vector<csmp::int32> > enb; //pfverts
  std::deque<std::vector<csmp::double64> > ncl; //pelmt
  std::map<size_t, csmp::int32> bnf; //bflags

  std::deque<csmp::int32>   et;
  csmp::VSet<2U> v;
};

Setup.cpp

#include "Setup.h"


Setup::Setup() {

  std::cout<<"Setup initialized."<<std::endl;

}

Setup::~Setup() {

}

void Setup::InitializeSetup() {


  for(size_t i = 0; i < this->eni.size(); i++) {

      this->npes.push_back(this->eni[i].size());
  }

  for(size_t i = 0; i < this->enb.size(); i++) {

    this->epes.push_back(this->enb[i].size());

  }
  this->v.Resize(this->et, npes, epes, this->ncl.size()); //This is the line that does not work
}

CSMPy.i

%module CSMPy

%{
#define SWIG_FILE_WITH_INIT
#include "stdlib.h"
#include <vector>
#include <deque>
#include <map>
#include "VSet.cpp"
#include "Setup.h"
#include "Number_Types.h"
%}

%include "Number_Types.h"

%include "std_map.i"
%include "std_vector.i"
%include "std_deque.i"

// Instantiate templates used by CSMPy
namespace std {
  %template() pair<size_t, csmp::int32>;
  %template() pair<size_t, csmp::double64>;

  %template() pair<size_t, vector<size_t> >;
  %template() pair<size_t, vector<csmp::int32> >;
  %template() pair<size_t, vector<csmp::double64> >;

  %template(Deque_SizeT) deque<size_t>;
  %template(Deque_Int) deque<csmp::int32>;

  %template(Vector_SizeT) vector<size_t>;
  %template(Vector_Int32) vector<csmp::int32>;
  %template(Vector_Double64) vector<csmp::double64>;

  %template(Deque_Double64) deque<csmp::double64>;

  %template(Deque_Vector_Int) deque<vector<csmp::int32> >;
  %template(Deque_Vector_SizeT) deque<vector<size_t> >;
  %template(Deque_Vector_Double64) deque<vector<csmp::double64> >;

  %template(Map_SizeT_Int) map< size_t, csmp::int32>;
  %template(Map_SizeT_Double64) map< size_t, csmp::double64>;

  %template(Map_SizeT_Vector_SizeT) map< size_t, vector<size_t> >;
  %template(Map_SizeT_Vector_Int) map< size_t, vector<csmp::int32> >;
  %template(Map_SizeT_Vector_Double64) map< size_t, vector<csmp::double64> >;
}
%include "Setup.h"

编辑2:

我做了nm -gC myLib.so

我发现了这个回声

__ZN4csmp4VSetILm2EE6ResizeERKNSt3__15dequeIiNS2_9allocatorIiEEEERKNS3_ImNS4_ImEEEESC_m

在c ++倾斜上告诉我:

csmp::VSet<2ul>::Resize(std::__1::deque<int, std::__1::allocator<int> > const&, std::__1::deque<unsigned long, std::__1::allocator<unsigned long> > const&, std::__1::deque<unsigned long, std::__1::allocator<unsigned long> > const&, unsigned long)
关于这一点的几点说明,我已经转而使用clang ++作为我的编译器并手动编译。我还在我的.i文件中放了#include“VSet.cpp”。 (参见上一篇文章中的编辑)

我现在在python中导入时出现此错误:

Symbol not found: __ZN4csmp5VData6InTextERSt14basic_ifstreamIcSt11char_traitsIcEE
  Referenced from: ./_CSMPy.so
  Expected in: flat namespace

我还创建了一个main来实例化该对象,并且对Initialize()的调用也有效。

3 个答案:

答案 0 :(得分:3)

找不到符号

__ZN4csmp4VSetILm2EE6ResizeERKSt5dequeIiSaIiEERKS2_ImSaImEESA_m

在.so。感谢Dave对此进行解组,我们现在知道它指的是

csmp::VSet<2ul>::Resize(
    const std::deque<int>&, 
    const std::deque<unsigned long> &, 
    const std::deque<unsigned long> &)

根据您发布的内容,您有两种类型的deques,这有点奇怪。

以下是一些尝试:

  1. 验证_CSMP.so是否链接到编译器附带的STL库,您可能必须在setup.py中指定一个额外的开关或字段。你说,当你没有调整大小时你的代码就可以了,所以这不太可能是问题所在。
  2. 打开setup.py中的详细输出,以便您可以看到编译和链接命令行参数
  3. 确保您在SWIG .i文件中包含%std_deque.i。您没有收到编译错误,因此这不太可能是问题。
  4. 验证您已在.i中使用deque<int>实例化%template(IntDeque) std::deque<int>,因为Python对C ++模板一无所知,模板不是类,而是一个配方,因此编译器可以创建一个上课。如果你真的使用int和unsigned long,你必须实例化它们。我只在你的代码中看到int和size_t。您不能假设size_t与unsigned long相同。
  5. 确认您的DLL包含unsigned int的此Resize方法的实例化。在您的C ++代码中。我认为您定义了size_t版本,或者是unsigned long意外?
  6. 关于#5:

    SWIG生成标头和源文件。在头文件中,它放置了与Python C API相关的函数,并将它们注册到Python解释器中,在这些函数的主体中,它确定了从库中调用的C / C ++函数。在DLL中找不到上面的Resize的事实表明SWIG认为需要Resize的这个重载,所以它是从它生成的函数中调用的,但你的C ++ lib没有实例化它。

    这怎么可能?在C ++ lib中,您有一个带有Resize方法的类模板。类模板的技巧是编译器只会为DLL中使用的方法生成代码(因此,如果您的类定义了5个方法,但是您的DLL只使用1,则不会为其他4个方法生成代码),如果您在库中显式实例化模板,则外。你可以通过声明

    来做到这一点
    template class VSet<2ul>; 
    

    (无论2ul代表什么)在您的C ++ DLL中,或者通过.i文件中的%template指令包装DLL。这将实例化VSet<2ul>的所有方法,因此Resize也将存在。 IF 由此生成的Resize具有参数deque<int>deque<unsigned long>。您的代码表明您假设size_t是unsigned int。如果size_t的typedefd为unsigned int,SWIG应该能够处理它,但可能存在错误。最好不要假设。您可以为unsigned int添加Resize重载。或者您可以在安装程序中创建一个内联扩展方法,获取两个unsigneld long deques并调用size_t版本。像

    这样的东西
    %template DequeULong std::deque<unsigned long>
    
    %extend Setup {
       void Resize(const DequeInt& a, const DequeULong& b) 
       {
           DequeSizet c;
           ... copy b into a DequeSizet
           Resize(a, c);
       }
    }
    

答案 1 :(得分:1)

问题很可能不是编译问题。您的头文件和实现文件之间的不匹配更有可能。标头承诺您没有实现的接口。如果您从未在C ++代码中调用该成员函数,则不会在独立的C ++应用程序中看到未定义的引用。

当您告诉SWIG包装该C ++标头时,标头和实现之间的不匹配会成为一个真正的问题。生成的SWIG代码包含对该未实现函数的引用。动态链接失败,因为永远不会定义该函数。

那是什么功能呢?查看错误消息:

Symbol not found: __ZN4csmp4VSetILm2EE6ResizeERKSt5dequeIiSaIiEERKS2_ImSaImEESA_m

这可以告诉你究竟缺少什么,但是以一种非常复杂(名称错误)的方式。复制该符号,打开终端窗口,然后发出命令echo <paste mangled name here> | c++filt

echo __ZN4csmp4VSetILm2EE6ResizeERKSt5dequeIiSaIiEERKS2_ImSaImEESA_m | c++filt

c++filt实用程序是Mac和Linux机器上非常有用的功能。在这种情况下,它会为您提供缺失符号的未标记名称。请参阅我对Schollii的回答的评论。

答案 2 :(得分:0)

我正在开始一个新的答案,因为VSet&lt; 2U&gt;的事实没有包裹使我的大部分其他答案与这个问题无关(虽然那里的一切都还是正确的)。并且Setup具有VSet&lt; 2U&gt;类型的数据成员的事实。与SWIG无关,因为您没有直接从Python访问Setup :: v。

验证安装程序在没有Python或SWIG的情况下工作:创建一个void main(),在其中实例化一个Setup并调用其InitializeSetup()方法,构建并运行。由于找不到符号,很可能会得到相同的运行时错误。

安装对象代码正在寻找

csmp::VSet<2ul>::Resize(
    const std::deque<int>&, 
    const std::deque<unsigned long> &, 
    const std::deque<unsigned long> &,
    unsigned long)

因此请验证您的DLL是否具有此符号:

~> nm -gC yourLib.so

可能没有。是否有其他已实例化的Resize重载?这可以给出一个线索。

编译器无法为VSet&lt; 2U&gt;实例化Resize可能有多种原因。例如,模板类的方法定义必须出现在.h中,否则编译器将如何知道要生成哪些代码?如果您告诉编译器编译VSet.cpp,除非您为特定类型显式实例化模板,否则它不会在.o中生成任何内容。然后.o将包含具有该类型的特定模板化类的类的对象代码。我喜欢让我的方法定义分开类定义,但之后我在.h中包含.cpp,因为.h的任何用户都需要包含.cpp,因此编译器可以生成正确的代码。对你而言,这意味着你将在VSet.h的底部有一个#include "VSet.cpp" // templated code