首页 > 解决方案 > Boost python没有看到内存归智能指针所有

问题描述

当下面的析构函数破坏它的向量元素时,我得到一个段错误触发器。最初它是一个vector<Parent>但我将其更改为vector<unique_ptr<Parent>>从那时起每次都会发生崩溃:

class Owner
{
    Owner() = default;
    ~Owner() = default;    // Seg faults

    void assignVec(std::vector<std::unique_ptr<Parent>>& vec)
    {
        _vec = std::move(vec);
    }

    std::vector<std::unique_ptr<Parent>> _vec;
};

每个向量元素子类型都是一个多态类,也继承自 boost::python::wrapper

class Child: public Parent, public boost::python::wrapper<Parent>
{
    Child();
    virtual ~Child() = default;
};

在哪里:

class Parent
{
    Parent() = default;
    virtual ~Parent() = default;
};

所以整个继承层次确实有虚拟析构函数。

GDB 回溯显示:

#0  0x00007ffff636b207 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:55
#1  0x00007ffff636c8f8 in __GI_abort () at abort.c:90
#2  0x00007ffff63add27 in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff64bf678 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:196
#3  0x00007ffff63b6489 in malloc_printerr (ar_ptr=0x7ffff66fb760 <main_arena>, ptr=<optimized out>, str=0x7ffff64bcd31 "free(): invalid pointer", action=3) at malloc.c:5004
#4  _int_free (av=0x7ffff66fb760 <main_arena>, p=<optimized out>, have_lock=0) at malloc.c:3843
#5  0x00007fffc373972f in Child::~Child (this=0x2742b10, __in_chrg=<optimized out>) at Child.h:23
#6  0x000000000045694e in std::default_delete<Parent>::operator() (this=0x11922e0, __ptr=0x2742b10) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/unique_ptr.h:81
#7  0x0000000000454c27 in std::unique_ptr<Parent, std::default_delete<Parent> >::~unique_ptr (this=0x11922e0, __in_chrg=<optimized out>) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/unique_ptr.h:274
#8  0x000000000045a882 in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> > > (__pointer=0x11922e0) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:98
#9  0x0000000000458f67 in std::_Destroy_aux<false>::__destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*> (__first=0x11922e0, __last=0x11922e8) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:108
#10 0x0000000000457636 in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*> (__first=0x11922e0, __last=0x11922e8) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:137
#11 0x000000000045584d in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*, std::unique_ptr<Parent, std::default_delete<Parent> > > (__first=0x11922e0, __last=0x11922e8)
    at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:206
#12 0x000000000049b53d in std::vector<std::unique_ptr<Parent, std::default_delete<Parent> >, std::allocator<std::unique_ptr<Parent, std::default_delete<Parent> > > >::~vector (this=0x7fffffffc4a8, __in_chrg=<optimized out>)
    at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_vector.h:567
#13 0x000000000048c677 in Owner::~Owner (this=0x7fffffffc4a8, __in_chrg=<optimized out>)

this在第 5 帧中打印确实显示了一个有效的对象。free()的第四帧源码为:

static void _int_free(mstate av, mchunkptr p, int have_lock)                      
{                                                                     
  INTERNAL_SIZE_T size;        /* its size */                         
  mfastbinptr*    fb;          /* associated fastbin */               
  mchunkptr       nextchunk;   /* next contiguous chunk */            
  INTERNAL_SIZE_T nextsize;    /* its size */                         
  int             nextinuse;   /* true if nextchunk is used */        
  INTERNAL_SIZE_T prevsize;    /* size of previous contiguous chunk */
  mchunkptr       bck;         /* misc temp for linking */            
  mchunkptr       fwd;         /* misc temp for linking */            

  const char *errstr = NULL;
  int locked = 0;

  size = chunksize(p);

  /* Little security check which won't hurt performance: the          
     allocator never wrapps around at the end of the address space.   
     Therefore we can exclude some size values which might appear
     here by accident or by "design" from some intruder.  */        
  if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)    
      || __builtin_expect (misaligned_chunk (p), 0))
    {  
      errstr = "free(): invalid pointer";
    errout:      
      if (have_lock || locked)
        (void)mutex_unlock(&av->mutex);                               
      malloc_printerr (check_action, errstr, chunk2mem(p), av);      // CRASHES HERE

有人对如何进行调试有任何建议吗?

更新:

我在单元测试中创建了一个小例子,创建Owner了一个向量,调用assignVec()并没有出现问题。然而,一旦向量被传入,其他任何东西都无法获得父内存。

更新2:

我们认为问题在于 boost python 需要被告知智能指针。显然 Boost Python 不支持 unique_ptr,我们正在努力让它识别 shared_ptr(Boost 和 std),即使使用典型的寄存器技术。

标签: c++movesmart-pointersunique-ptrboost-python

解决方案


根据您提供的代码并且假设boost::python这里没有错误,我猜您对移动语义的使用可能是原因:

void assignVec(std::vector<std::unique_ptr<Parent>>& vec)
    {
        _vec = std::move(vec);
    }

在这里,您正在从左值引用到向量,vector&再到您的成员。问题是:通常只从 r-values 引用 ( vector&&) 移动,因为它们不能再访问,因为唯一绑定到临时对象,或者如果一个人通过创建 r/x-values 引用明确地确认移动,就像您使用std::move.

问题是:我敢打赌,assignVec 的调用者可能不知道这一点,因为那为什么你还没有在签名中使用 r 值引用,所以调用者必须std::move显式地使用?我的假设是,您的调用者没有,并且正在做的不仅仅是一件合法的从价值观转移的事情:破坏它们。

当然,您会问自己,为什么它会在析构函数中中断?根据我的经验,分段错误会在原因之后出现一些表达式,在这种情况下,我会说调用者的未定义行为assignVec仍然使用给定的向量assignVec


推荐阅读