Перехват вызванной Rust паники как исключения C++

Я тестирую библиотеки Rust, которые могут вызвать панику, используя их в коде на C++. У меня есть следующая библиотека:

Cargo.toml:

[package]
name = "boom"
version = "0.1.0"
edition = "2018"

[dependencies]
libc = "0.2"

[lib]
crate-type=["staticlib"]

src/lib.rs:

#[no_mangle]
pub extern "C" fn boom() {
    panic!("Oh no, something went wrong!");    
}

Я создаю его с помощью cargo build и использую результат в следующем коде C++ (файл boom.cpp):

// g++ -g boom.cpp -L./target/debug -lboom -o boom

extern "C" {
    void boom();
}

int main() {
    boom();
}

Результат:

thread '<unnamed>' panicked at src/lib.rs:3:5:
Oh no, something went wrong!
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5

Все идет нормально.

Я пытаюсь отразить эту панику в своем коде на C++. Я пробовал следующее:

// g++ -g boom.cpp -L./target/debug -lboom -o boom

#include <stdexcept>

extern "C" {
    void boom();
}

int main() {
    try
    {
      boom();
    }
    catch(const std::exception& e)
    {
      printf("exception captured\n");
    }
}

но результат тот же, вместо ожидаемого exception captured сообщения.

Есть ли способ в коде C++ отразить панику, возникающую в библиотеке Rust? Или, наоборот, есть ли способ превратить панику Rust в перехватываемое исключение C++?

«Я тестирую библиотеки Rust, и это может вызвать панику». Это абсолютно непреодолимо? Паники не предназначены для перехвата, как исключения C++. Самый близкий механизм исправления ошибок в Rust — это функции, возвращающие Result<T, E>. Соответствующее чтение: stackoverflow.com/questions/30232890/… stackoverflow.com/questions/30824258/…

E_net4 18.04.2024 11:54

из того, что я прочитал, просматривая doc.rust-lang.org/book/… паника НЕ ​​является исключением — ее нельзя поймать.

Christian Stieber 18.04.2024 12:00

@E_net4 спасибо за ваш отзыв! Помимо упрощенных примеров в моем вопросе выше, я боюсь, что настоящая библиотека Rust, которую я использую (github.com/mozilla/jexl-rs), работает таким образом и в некоторых обстоятельствах паникует (вместо того, чтобы возвращать правильное значение). ошибки). Итак мотивация моего вопроса.

fgalan 18.04.2024 12:02

@ChristianStieber спасибо за отзыв! Я понимаю, что панику нельзя поймать, но в этом случае у меня нет выбора (кроме разветвления и изменения библиотеки, не реализованной мной, что сложно, см. комментарий выше). Насколько мне известно, Rust предоставляет некоторый механизм для перехвата паники (panic::catch_unwind, как показано здесь), но я не знаю, как использовать его для генерации исключения C++, которое будет перехвачено в моей программе на C++ с помощью библиотеки Rust ( если возможно)

fgalan 18.04.2024 12:05

Библиотека, которая имеет тенденцию паниковать при определенных входных данных, является либо признаком ошибки, либо плохо спроектированного API. Учитывая, что рассматриваемая библиотека имеет открытый исходный код, не кажется надуманным направить вклад туда, где это необходимо. В противном случае вам понадобится дополнительный уровень очистки ввода. В крайнем случае есть catch_unwind. Тогда вторая часть вопроса, которая, на мой взгляд, более интересна, заключается в том, как превратить ошибку Rust в исключение C++.

E_net4 18.04.2024 12:23

Имейте в виду, что я никогда не использовал Rust, а как насчет doc.rust-lang.org/std/panic/fn.set_hook.html ? Это не вызовет исключения, но вы, вероятно, могли бы вызвать некоторую функцию в своем коде C++ с помощью "PanicInfo"... хотя я не уверен, можно ли вообще вернуть исходный кадр вызова, я бы попробовал бросьте изнутри эту функцию-перехватчик, просто чтобы посмотреть, что произойдет...

Christian Stieber 18.04.2024 12:26

Rust объявляет, что выход паники за пределы FFI является неопределённым поведением. См. также stackoverflow.com/questions/77876748/…

Sebastian Redl 18.04.2024 12:29

Как отмечает Себастьян, вы не можете паниковать через границы FFI, FFI говорят на C (или на самом деле ABI платформы), и даже если бы они это сделали (Windows может поддерживать SEH), я не верю, что Rust использует это для паники. Итак, что вам следует делать, если вы не можете заставить восходящий поток исправить панику и не можете исправить свой собственный код, чтобы избежать ее запуска, это std::panic::catch_unwind в extern и вернуть безопасный код ошибки или структурированный ошибка.

Masklinn 18.04.2024 13:33

Чтобы смешать Rust и C++, я бы установил panic = 'abort' в вашем Cargo.toml. Паника не является исключением.

Eljay 18.04.2024 16:06

@Jmb спасибо за ваш отзыв! Вы правы: после тщательного изучения библиотека jexl-rs ведет себя правильно... это моя собственная библиотека-оболочка между jexl-rs и моей программой на C++, которой не было. Поэтому я изменю свою библиотеку, чтобы использовать шаблон Result<T, E>, принимая во внимание ценные отзывы, представленные в этом посте.

fgalan 19.04.2024 08:26
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
11
137
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

C не имеет исключений

Для Rust нет C++ FFI, есть только C FFI.

В C нет исключений и, следовательно, нет специального ABI для распространения исключений.

В результате не существует стандартного механизма распространения исключений из Rust в C (или C в Rust), что не сильно поможет, поскольку в C все равно нет способа взаимодействовать с исключениями.

Исключения не должны пересекать языковые границы

Здесь тоже нет никакой специфики Rust. Позволить исключениям C++ пересекать кадры стека C — это уже авантюра.

В любом случае кадры стека C не генерируют никакого кода для обработки раскрутки и, следовательно, не выполняют очистку при раскрутке во время распространения исключения, что приводит к неопределенному поведению. На практике последствия могут быть самыми разными: от «просто работает» до «протекающего» и «испорченного состояния».

Если инструментальная цепочка не гарантирует явную совместимость, исключения не должны пересекать границы языка.

Так как же справиться с паникой Rust в C++?

Путем их перевода вручную или автоматически на границе FFI.

То есть от Rust к C++:

  1. На стороне Rust используйте catch_unwind, чтобы перевести исключение в ошибку.
  2. На стороне C++ обработайте ошибку (возможно, выдавая ее снова).

А если вы забудете, надеюсь, RFC 2945 спасет вас (путем прерывания), когда вы испытываете.

От C++ к Rust:

  1. На стороне C++ используйте catch, чтобы преобразовать исключение в ошибку.
  2. На стороне Rust обработайте ошибку (возможно, снова паникуем).

А если вы забудете, вы формально войдете в страну неопределенного поведения. Удачи.

Другие вопросы по теме