地图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好吧,为了简洁起见,这里省略这个解决方案的另一个原因是: