草庐IT

c++ - 在 C++ 静态 boost::shared_ptr 中保存 python 生成的值

coder 2024-02-12 原文

在使用 Boost.Python 和 C++ 时,有时我们会创建使用类本身和 boost::shared_ptr<> 绑定(bind)的类。版本。出于多种原因,这非常方便,可以在很多地方使用。 但是,当boost::python 时,该机制似乎无法可靠地工作。返回 boost::shared_ptr<>为在 Python 中生成并记录在 C++ 静态变量中的值。

通常,我希望返回 boost::shared_ptr<>持有一个特殊的删除器来处理这个问题,但事实并非如此。似乎发生的是返回的 boost::shared_ptr只是包装一个指向在 Python 中产生的值的指针,没有任何关于删除的特殊考虑。这会导致来自双重删除的一致崩溃(一个来自 Python 解释器本身,一个来自 C++ 静态)——或者至少它看起来像这样。

要使用下面的代码重现此行为,请创建一个 test.cc如下所示的文件并使用以下脚本进行测试。

#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>

struct A {
  std::string _a;
  A(std::string a): _a(a) {}
  std::string str() { return _a; }
};

static boost::shared_ptr<A> holder(new A("foo"));

static void set_holder(boost::shared_ptr<A> a_ptr) {
  holder = a_ptr;
}

static boost::shared_ptr<A> get_holder() {
  return holder;
}

BOOST_PYTHON_MODULE(test)
{
  using namespace boost::python;

  class_<A, boost::shared_ptr<A> >("A", init<std::string>())
    .def("__str__", &A::str)
    ;

  def("set_holder", &set_holder);
  def("get_holder", &get_holder);
}

使用以下 Python 测试程序:

import test
print(str(test.get_holder()))
test.set_holder(test.A('bar'))
print(str(test.get_holder()))

在 Linux(ubuntu 12.10,Python 2.7 和 Boost 1.50)下编译(使用 g++ -I/usr/include/python2.7 -shared -fpic test.cc -lboost_python -lpython2.7 -o test.so)并运行上述程序(python test.py),得到以下堆栈跟踪:

#0  0x000000000048aae8 in ?? ()
#1  0x00007fa44f85f589 in boost::python::converter::shared_ptr_deleter::operator()(void const*) () from /usr/lib/libboost_python-py27.so.1.50.0
#2  0x00007fa44fa97cf9 in boost::detail::sp_counted_impl_pd<void*, boost::python::converter::shared_ptr_deleter>::dispose() ()
   from /remote/filer.gx/home.active/aanjos/test.so
#3  0x00007fa44fa93f9c in boost::detail::sp_counted_base::release() ()
   from /remote/filer.gx/home.active/aanjos/test.so
#4  0x00007fa44fa9402b in boost::detail::shared_count::~shared_count() ()
   from /remote/filer.gx/home.active/aanjos/test.so
#5  0x00007fa44fa94404 in boost::shared_ptr<A>::~shared_ptr() ()
   from /remote/filer.gx/home.active/aanjos/test.so
#6  0x00007fa450337901 in __run_exit_handlers (status=0, 
    listp=0x7fa4506b46a8 <__exit_funcs>, run_list_atexit=true) at exit.c:78
#7  0x00007fa450337985 in __GI_exit (status=<optimized out>) at exit.c:100
#8  0x00007fa45031d774 in __libc_start_main (main=0x44b769 <main>, argc=2, 
    ubp_av=0x7fffaa28e2a8, init=<optimized out>, fini=<optimized out>, 
    rtld_fini=<optimized out>, stack_end=0x7fffaa28e298) at libc-start.c:258
#9  0x00000000004ce6dd in _start ()

这表示在静态析构函数中发生了双重删除。这种行为在不同平台之间似乎是一致的。

问题:是否可以在不复制 boost::python 的返回值的情况下实现所描述的行为?在上面的玩具示例中,这很简单,但在我真正的问题中,是 A 的深层拷贝将是不切实际的。

最佳答案

你遇到的问题是 shared_ptr 的破坏发生在 python 完成之后。看看:

我建议封装shared_ptr,它没有额外的清理代码。 不过有四个解决方案:

    #include <boost/python.hpp>
    #include <boost/shared_ptr.hpp>
    #include <iostream>

struct A {
  std::string _a;
  A(std::string a): _a(a) {}
  ~A() { std::cout << "Destruct: " << _a << std::endl; }
  std::string str() { return _a; }
};

void python_exit();

static boost::shared_ptr<A> holder(new A("foo"));

static boost::shared_ptr<A> get_holder() {
  return holder;
}

static void set_holder(boost::shared_ptr<A> a_ptr) {
  // The shared_ptr comes with python::converter::shared_ptr_deleter
  holder = a_ptr;
}


// Fix 1: Cleanup while python is running
// ======================================

void reset_holder() {
   std::cout << "reset" << std::endl;
   holder.reset(new A("holder without shared_ptr_deleter"));
}


// Fix 2: The shared pointer is never deleted (which is a memory leak of a
//        global varialbe). The contained object is destructed, below.
// =========================================================================

static boost::shared_ptr<A>* holder_ptr = new boost::shared_ptr<A>(
    new A("foo_ptr"));

static boost::shared_ptr<A> get_holder_ptr() {
  return *holder_ptr;
}

static void set_holder_ptr(boost::shared_ptr<A> a_ptr) {
  // Note: I know, it's no good to do that here (quick and dirty):
  Py_AtExit(python_exit);
  // The shared_ptr comes with python::converter::shared_ptr_deleter
  *holder_ptr = a_ptr;
}

void python_exit() {
   std::cout << "\n"
     "Since Python’s internal finalization will have completed before the\n"
     "cleanup function, no Python APIs should be called in or after exit.\n"
     "The boost::python::shared_ptr_deleter will do so, though.\n"
     << std::endl;
   // Destruction but no deallocation.
   holder_ptr->get()->~A();
}


// Fix 3: Put a finalizer object into a module.
// =========================================================================

static boost::shared_ptr<A> holder_finalizer(new A("foo_finalizer"));


struct PythonModuleFinalizer
{
    ~PythonModuleFinalizer();
};

PythonModuleFinalizer::~PythonModuleFinalizer() {
    std::cout << "PythonModuleFinalizer" << std::endl;
    holder_finalizer.reset(
        new A("holder_finalizer without shared_ptr_deleter"));
}

static boost::shared_ptr<A> get_holder_finalizer() {
  return holder_finalizer;
}

static void set_holder_finalizer(boost::shared_ptr<A> a_ptr) {
  // The shared_ptr comes with python::converter::shared_ptr_deleter
  holder_finalizer = a_ptr;
}


// Fix 4: Encapsulate the shared_ptr
// =========================================================================

class B {
    private:
    struct I {
        std::string b;
        I(const std::string& b): b(b) {}
        ~I() { std::cout << "Destruct: " << b << std::endl; }
    };

    public:
    B(std::string b): s(new I(b)) {}
    std::string str() { return s.get()->b; }

    private:
    boost::shared_ptr<I> s;
};

static B holder_encapsulate("foo_encapsulate");


static B get_holder_encapsulate() {
  return holder_encapsulate;
}

static void set_holder_encapsulate(B b) {
  holder_encapsulate = b;
}


BOOST_PYTHON_MODULE(test)
{
  using namespace boost::python;

  class_<A, boost::shared_ptr<A> >("A", init<std::string>())
    .def("__str__", &A::str)
    ;

  def("set_holder", &set_holder);
  def("get_holder", &get_holder);
  def("reset_holder", &reset_holder);

  def("set_holder_ptr", &set_holder_ptr);
  def("get_holder_ptr", &get_holder_ptr);

  object finalizer_class = class_<PythonModuleFinalizer
      ("PythonModuleFinalizer", init<>());
  object finalizer = finalizer_class();
  scope().attr("ModuleFinalizer") = finalizer;
  def("set_holder_finalizer", &set_holder_finalizer);
  def("get_holder_finalizer", &get_holder_finalizer);

  class_<B>("B", init<std::string>())
    .def("__str__", &B::str)
  ;
  def("set_holder_encapsulate", &set_holder_encapsulate);
  def("get_holder_encapsulate", &get_holder_encapsulate);

}

python 文件:

import test
print(str(test.get_holder()))
test.set_holder(test.A('bar'))
print(str(test.get_holder()))
test.reset_holder()

print(str(test.get_holder_ptr()))
test.set_holder_ptr(test.A('bar_ptr'))
print(str(test.get_holder_ptr()))

print(str(test.get_holder_finalizer()))
test.set_holder_finalizer(test.A('bar_finalizer'))
print(str(test.get_holder_finalizer()))

print(str(test.get_holder_encapsulate()))
test.set_holder_encapsulate(test.B('bar_encapsulate'))
print(str(test.get_holder_encapsulate()))

测试的输出是:

foo
Destruct: foo
bar
reset
Destruct: bar
foo_ptr
Destruct: foo_ptr
bar_ptr
foo_finalizer
Destruct: foo_finalizer
bar_finalizer
foo_encapsulate
Destruct: foo_encapsulate
bar_encapsulate
PythonModuleFinalizer
Destruct: bar_finalizer

Since Python’s internal finalization will have completed before the
cleanup function, no Python APIs should be called in or after exit.
The boost::python::shared_ptr_deleter will do so, though.

Destruct: bar_ptr
Destruct: bar_encapsulate
Destruct: holder without shared_ptr_deleter

关于c++ - 在 C++ 静态 boost::shared_ptr 中保存 python 生成的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18184209/

有关c++ - 在 C++ 静态 boost::shared_ptr 中保存 python 生成的值的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby-on-rails - rails : keeping DRY with ActiveRecord models that share similar complex attributes - 2

    这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby​​类,但是我如何得到ActiveRecord关联这个类模型

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  6. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  7. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  8. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  9. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  10. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

随机推荐