C++与python交互库-pybind11的返回值策略 return_value_policy

2023-05-16

pybind11的返回值策略 return_value_policy

  • 一、返回值策略的必要性
  • 二、一个导致crash的例子
  • 三、所有的返回值策略的探讨
  • 四、补充说明

一、返回值策略的必要性

  C++和python使用根本上就不一样的内存管理方法和对象生命周期管理方法。
  C++是对栈变量按照作用域来析构,对堆变量使用手动delete来析构。Python则是使用基于计数的垃圾回收机制。当提供C++函数接口给python时,如果返回类型不是基本类型,这种内存管理的差异会产生一些问题。
  只是看类型信息,看不出来Python是否应该接管返回值的所有权并最终回收它的资源,或是由C++端来处理。因为这个原因,pybind11提供了一些返回值策略标记传递给 module_::def()和class_::def()函数。
  默认的策略是return_value_policy::automatic(自动管理返回值)。

二、一个导致crash的例子

  返回值策略非常容易犯错,所以有必要真正弄懂它们。考虑下面一个例子:

struct Person{
	int age;
	int gender;
};
Person* one_person = new Person();
/* Function declaration */
Person* get_one_person() { return one_person ; /* (pointer to a static data structure) */ }

/* Binding code */
m.def("get_one_person", &get_one_person); // <-- 严重危险行为,可能导致程序crashed

  这里会发生什么?如果Python端调用了get_one_person(),那么返回类型被包装成一个python可用的类型。在这个场景,默认的返回值策略是return_value_policy::automatic(返回值自动管理),将会导致pybind11接管静态对象one_person的所有权。

  当python的垃圾回收器最终删除python包装时,pybind11也会尝试析构这个C++对象(使用delete操作符)。这时整个应用程序最终会crash,尽管原因非常隐蔽,其实是静态对象内存损坏。

  在上面这个例子中,应该使用return_value_policy::reference(引用返回值)策略。这种全局的静态对象one_person只是被python引用,而没有任何隐含的所有权转移。

m.def("get_one_person", &get_one_person, py::return_value_policy::reference);

三、所有的返回值策略的探讨

  很多时候如果没有正确使用返回值策略,会导致内存泄露。作为使用pybind11的开发者,必须熟悉所有的返回值策略,清楚知道什么情况该用什么。下面是所有的返回值策略。

返回值策略C++返回值类型描述
return_value_policy::take_ownership(接管返回值所有权)指针引用一个已经存在的对象然后接管它的所有权。Python会调用析构函数当这个对象的引用计数降至零时。警告:当C++端也去析构时,或者这个对象本身不是堆变量时,会发生未定义行为(极可能导致程序crash)
return_value_policy::copy(复制返回值)值(左值)创建一个返回值的复制(会调用这个对象的复制构造函数),python端将使用这个复制对象。这个策略相对安全,因为两个对象的生命周期是分开的。
return_value_policy::move(移动返回值)值(右值)使用std::move来移动返回值到一个新对象中(会调用这个对象的移动构造函数),python端将使用这个新对象。这个策略相对安全,因为两个对象(移动来源和目标)的生命周期是分开的。对这个策略的理解必须要理解C++11引入的右值和移动语义概念。这个策略实质上也是接管了原对象所代理数据的所有权。
return_value_policy::reference(引用返回值)指针引用一个已经存在的对象,并不接管它的所有权。C++端负责管理这个对象的生命周期和析构。警告:当C++端析构一个仍在被python使用的对象时,会发生未定义行为。
return_value_policy::reference_internal(内部引用返回值)指针指示返回值的生命周期是绑定在父对象的生命周期的,即绑在这个属性或方法对应的this、self变量上。在内部实现上,这个策略类似return_value_policy::reference(引用返回值),但是额外增加了一个keep_alive<0, 1>调用策略,能够防止父对象被垃圾回收器回收只要这个返回值还在被python引用。这种策略是设置属性的方法getters 的默认策略,像def_property、def_readwrite。
return_value_policy::automatic(自动管理返回值)指针或值当返回值是指针时,该策略就使用return_value_policy::take_ownership(接管返回值所有权)。当返回值是左值时,使用return_value_policy::copy(复制返回值)。当返回值是右值时,使用return_value_policy::move(移动返回值)。这个策略是py::class_包装类型的默认策略。
return_value_policy::automatic_reference(自动引用返回值)指针或值区别于上面的自动管理返回值,这个策略对指针返回值使用return_value_policy::reference(引用返回值)策略,并不去接管它的所有权。这个是在python端调用那些C++生成的函数时,函数参数传递的默认策略。普通用户几乎很少用到。

  返回值策略能应用在属性上:

class_<MyClass>(m, "MyClass")
    .def_property("data", &MyClass::getData, &MyClass::setData,
                  py::return_value_policy::copy);

  虽然上面的代码同时为getter和setter函数应用了返回值策略,但实际上setter并不关心返回值策略,只是一个语法糖。也有另一种写法,目标参数可以通过cpp_function 构造函数来传递。

class_<MyClass>(m, "MyClass")
    .def_property("data"
        py::cpp_function(&MyClass::getData, py::return_value_policy::copy),
        py::cpp_function(&MyClass::setData)
    );

四、补充说明

  上述策略的一个重要方面是,它们只应用于pybind11以前没有见过的实例,在这种情况下,该策略理清了关于返回值的生命周期和所有权的基本问题。当pybind11已经知道实例(根据它在内存中的类型和地址标识)时,它将返回现有的Python对象包装器,而不是创建一个新的副本。即是针对新变量而不是已有的变量。

  作为指定调用策略和生命周期管理逻辑的另一种选择,可以考虑使用智能指针。智能指针可以判断一个对象是否仍然被C++或Python引用,这通常会消除可能导致崩溃或未定义行为的不一致性。对于返回智能指针的函数,不需要指定返回值策略。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++与python交互库-pybind11的返回值策略 return_value_policy 的相关文章

随机推荐

  • 树莓派4b利用官方软件安装系统

    目录 资源准备一 安装系统1 格式化TF卡2 安装系统 二 连接启动1 连接2 登陆3 开启vnc 总结 资源准备 硬件 xff1a 树莓派4b板子 8G以上TF卡 读卡器 软件 xff1a 官方安装软件 xff1a Raspberry P
  • 解决树莓派4b开启VNC报错无法正确显示桌面

    开启vnc报错 原因 xff1a 由于分辨率太小 xff0c 无法显示 解决办法 xff1a 在终端输入以下内容 sudo raspi config 选择Display Options进入 选择VNC Resolution 选择除第一个分辨
  • js中数组内删除指定对象

    先上代码 xff01 span class token comment 因我使用的是xm select组件 xff0c 所以自带change xff08 即新变化的对象 xff09 和arr xff08 选中的所有对象 xff09 span
  • 【JDK1.8 新特性】Lambda表达式

    1 什么是Lambda表达式 xff1f Lambda 是一个匿名函数 xff0c 我们可以把 Lambda 表达式理解为是一段可以传递的代码 xff08 将代码像数据一样进行传递 xff09 使用它可以写出更简洁 更灵活的代码 作为一种更
  • 封装axios请求

    1 引入所需模块 引入axios模块 import axios from 39 axios 39 使用qs模块 xff0c 进行文件序列号化操作 import qs from 39 qs 39 2 判断当前环境 环境变量 let baseU
  • Matlab的Gpu加速(CUDA cudnn)

    多CUDA版本的切换 xff0c 及Matlab版本对应的CUDA版本 学习笔记 Matlab的Gpu加速 安装CUDA和cudnn时 xff0c 踩了不少坑 xff0c 总结如下几个经验 xff1a 1 显卡驱动的版本与CUDA是一对多的
  • win10添加新硬盘

    win10添加新硬盘 前言 前言 小白指导 xff0c 白到不能再白了 xff01 xff01 xff01 直接上图 关于文件系统介绍 文件系统是系统对文件的存放排列方式 xff0c 不同格式的文件系统关系到数据是如何在磁盘进行存储 xff
  • idea2020创建maven javaweb项目并运行 示例

    idea2020创建maven javaweb项目并运行 一 项目的创建二 pom xml简单介绍三 添加tomcat插件以方便运行项目1 在pom xml文件里加入以下配置 四 创建servlet并 运行项目1 创建Java文件夹并在文件
  • C++实现算法服务的一些经验总结

    文章目录 一 算法模型调用的几种方式二 python算法服务的优缺点三 C 43 43 算法服务的优缺点四 C 43 43 算法服务的几种实现方式五 C 43 43 基于http的算法服务实现思路六 记录调用 xff1a 日志 数据库七 性
  • Java中使用jedis操作redis(使用maven)

    jedis的使用 前言一 简单使用示例1 快速入门2 操作五种数据格式 二 jedis连接池 xff1a jedisPool1 示例2 jedies详细配置 三 创建连接池工具类1 jedis properties2 JediesPoolU
  • maven中使用jdbc

    用到的有数据库连接池 JDBCTemplate 一 首先导入jar包 lt mysql驱动 xff0c 使用对应版本jar包依赖 gt lt dependency gt lt groupId gt mysql lt groupId gt l
  • 解决风扇狂转不止的好方法

    这两天风扇狂转不止 xff0c 真让我头疼 xff0c 现在弄好了 安装一个软件 软件官网 我的是thinkpade580 xff0c 对其他电脑生不生效我不清楚 xff08 但可以一试 xff09 下载后直接安装即可 有个地方实时显示着温
  • 安装hadoop3.x版本踩坑

    报错 xff1a ERROR Attempting to operate on hdfs namenode as root ERROR but there is no HDFS NAMENODE USER defined Aborting
  • 报错:Description: No bean of type ‘org.apache.shiro.realm.Realm‘ found

    springboot整合shiro的时候出现了报错 报错信息 xff1a Description No bean of type 39 org apache shiro realm Realm 39 found Action Please
  • 计算机网络课后习题概略

    计算机网络 课后习题 第一章 概述 1 01 计算机网络可以向用户提供哪些服务 xff1f 服务功能或作用数据传输网络间个计算机之间互相进行信息的传递 资源共享进入网络的用户可以对网络中的数据 软件和硬件实现共享 分布处理功能通过网络可以把
  • Kittle案例(Excel输入,生成记录,生成随机数,获取系统信息,排序记录,去除重复记录,替换NULL值)

    一 Excel输入 1 创建转换 将要处理的文件保存到Excel表中 xff0c 然后打开kettle创建一个转换并命名 xff0c 拖入 Excel输入 控件 2 配置Excel输入控件 浏览导入Excel文件 单击 浏览 B 按钮 xf
  • 第七章 数据加载

    一 全量加载 从技术角度来说 xff0c 全量加载比增量加载的操作要简单很多 xff0c 即只需要在数据加载之前 xff0c 将目标数据表进行清空 xff0c 再将源数据表中的数据全部加载到目标表中 案例实现 xff1a 数据装备 xff1
  • 第八章 综合案例——构建DVD租赁商店数据仓库(1)

    一 加载日期数据至日期维度表 操作介绍 xff1a 下面通过Kettle工具加载日期数据至dim date日期维度表 1 打开Kettle工具 xff0c 创建转换 使用Kettle工具 xff0c 创建一个转换load dim date
  • 第八章 综合案例——构建DVD租赁商店数据仓库(2)

    一 加载用户数据至用户维度表 操作介绍 xff1a 通过Kettle工具加载用户数据至用户维度表dim customer 1 打开Kettle工具 xff0c 创建转换load dim customer 使用Kettle工具 xff0c 创
  • C++与python交互库-pybind11的返回值策略 return_value_policy

    pybind11的返回值策略 return value policy 一 返回值策略的必要性二 一个导致crash的例子三 所有的返回值策略的探讨四 补充说明 一 返回值策略的必要性 C 43 43 和python使用根本上就不一样的内存管