地图C++;结果的例外情况 我正在编写一个锈库,它是C++库上的包装器。< /P> 这里是C++的一面: #定义结果(类型、名称)类型定义结构{type value;const char*message;}名称 外部“C” { 结果(加倍,结果加倍); ResultDouble myFunc() { 尝试 { 返回ResultDouble{value:cv::someOpenCvMethod(),消息:nullptr}; } 捕获(cv::异常和e) { const char*err_msg=e.what(); 返回ResultDouble{value:0,message:err\u msg}; } } }
和相应的锈蚀面:地图C++;结果的例外情况 我正在编写一个锈库,它是C++库上的包装器。< /P> 这里是C++的一面: #定义结果(类型、名称)类型定义结构{type value;const char*message;}名称 外部“C” { 结果(加倍,结果加倍); ResultDouble myFunc() { 尝试 { 返回ResultDouble{value:cv::someOpenCvMethod(),消息:nullptr}; } 捕获(cv::异常和e) { const char*err_msg=e.what(); 返回ResultDouble{value:0,message:err\u msg}; } } },c++,opencv,rust,ffi,C++,Opencv,Rust,Ffi,和相应的锈蚀面: #[repr(C)] 结构结果{ 值:T, 信息:*mut c_char, } 外部“C”{ fn myFunc()->CResult; } pub fn my_func()->结果 LI>我可以在C++边使用* const char,但是在锈迹一上使用*我需要它,因为CString::from_raw需要可变引用 关于的文档已经回答了问题的第一部分: “只能使用先前通过调用CString上的_raw获得的指针调用此函数。” 试图使用一个指向不是由CString创建的字符串的
#[repr(C)]
结构结果{
值:T,
信息:*mut c_char,
}
外部“C”{
fn myFunc()->CResult;
}
pub fn my_func()->结果
<> LI>我可以在C++边使用* const char,但是在锈迹一上使用*我需要它,因为CString::from_raw需要可变引用
关于的文档已经回答了问题的第一部分:
“只能使用先前通过调用CString
上的_raw获得的指针调用此函数。”
试图使用一个指向不是由CString
创建的字符串的指针在这里是不合适的,它会吃掉您的衣物
我应该改用CStr吗?如果是,我应该如何管理它的生命周期?我应该释放这个内存还是它有静态生存期
如果返回的C样式字符串保证有一个静态生存期(如中所述),那么您可以从中创建一个&'static CStr
,并返回该字符串。但是,情况并非如此:包含多个成员,其中一些成员拥有字符串对象。一旦程序离开myFunc
的作用域,捕获的异常对象e
将被销毁,因此,来自what()
的任何内容都将失效
const char* err_msg = e.what();
return ResultDouble{0, err_msg}; // oops! a dangling pointer is returned
虽然可以跨越外国金融机构边界转移价值,但所有权的责任应始终停留在该价值的来源。换句话说,如果C++代码正在创建异常,我们希望将这些信息提供给RIST代码,那么C++代码必须保留该值并最终释放它。我冒昧地选择了下面一种可能的方法
通过以下操作,我们可以重新实现myFunc
,将字符串存储在动态分配的数组中:
#包括
ResultDouble myFunc()
{
尝试
{
返回ResultDouble{value:cv::someOpenCvMethod(),消息:nullptr};
}
捕获(cv::异常和e)
{
const char*err_msg=e.what();
自动len=std::strlen(err_msg);
自动保留_err=新字符[len+1];
标准::strcpy(保留错误、错误消息);
返回ResultDouble{value:0,message:retained\u err};
}
}
这使得我们返回一个指向有效内存的指针。然后,一个新的公共功能必须公开,以获得免费的结果:
//在外部“C”中
无效结果(结果加倍*res){
删除[]res->消息;
}
在Rust land中,我们将使用中描述的方法保留同一字符串的副本。完成后,我们不再需要result
的内容,因此可以通过对free\u result
的FFI函数调用来释放它。处理to_str()
的结果而不进行展开
留给读者作为练习
extern "C" {
fn myFunc() -> CResult<c_double>;
fn free_result(res: *mut CResult<c_double>);
}
pub fn my_func() -> Result<f64, String> {
let result = unsafe {
myFunc()
};
if result.message.is_null() {
Ok(result.value)
} else {
unsafe {
let s = std::ffi::CStr::from_ptr(result.message);
let str_slice: &str = c_str.to_str().unwrap();
free_result(&mut result);
Err(str_slice.to_owned())
}
}
}
extern“C”{
fn myFunc()->CResult;
fn自由结果(结果:*多个结果);
}
pub fn my_func()->结果{
让结果=不安全{
myFunc()
};
如果result.message.is_null(){
Ok(result.value)
}否则{
不安全{
设s=std::ffi::CStr::from_ptr(result.message);
让str_切片:&str=c_str.to_str().unwrap();
自由结果(&mut结果);
错误(str_slice.to_owned())
}
}
}
上的文档已经回答了问题的第一部分:“只能使用先前通过调用CString上的_raw
获得的指针来调用它。”。然后,将这个问题缩小为从C返回拥有的字符串到RIST代码中。在C++中,异常及其内容已经自动释放。what
成员函数通常要么返回静态字符串,要么返回指向先前构建并保留为异常成员的字符串的指针。在OpenCV中,似乎是后者。我担心返回那个指针是不安全的。由于try
和catch
不是用C语言编写的,所以try
/catch
是否允许在extern“C”
部分中使用try
,消息:nullptr}代码>编译?我不认为命名这样的参数是有效的C++。@ AlxSukkoSky:这不是C初始化语法。指定的初始值设定项看起来像{.value=…,.message=…}
。如果您想要C字符串,我建议您使用类似C的代码。可以重写整个catch子句:returnresultdouble{0,std::strdup(e.what())}
,则必须将free\u result
的主体重写为std::free(res->message)代码>。您在生锈一侧创建的stru切片
从未使用过。。。我想这是剩下的。或者有一种混淆,因为s
不是String
@MatthiewM,我故意避免strdup
,因为它不是ISO C(尽管它在实践中应该可以工作,好吧)。至于stru切片
,是的,我想返回它而不是s
。这是固定的。@E_net4不错。然而,你给读者的练习
已经在以下问题中得到了解决:)@AlexZhukovskiy好吧,为了简洁起见,这里省略这个解决方案的另一个原因是: