Я тестирую библиотеки 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++?
из того, что я прочитал, просматривая doc.rust-lang.org/book/… паника НЕ является исключением — ее нельзя поймать.
@E_net4 спасибо за ваш отзыв! Помимо упрощенных примеров в моем вопросе выше, я боюсь, что настоящая библиотека Rust, которую я использую (github.com/mozilla/jexl-rs), работает таким образом и в некоторых обстоятельствах паникует (вместо того, чтобы возвращать правильное значение). ошибки). Итак мотивация моего вопроса.
@ChristianStieber спасибо за отзыв! Я понимаю, что панику нельзя поймать, но в этом случае у меня нет выбора (кроме разветвления и изменения библиотеки, не реализованной мной, что сложно, см. комментарий выше). Насколько мне известно, Rust предоставляет некоторый механизм для перехвата паники (panic::catch_unwind, как показано здесь), но я не знаю, как использовать его для генерации исключения C++, которое будет перехвачено в моей программе на C++ с помощью библиотеки Rust ( если возможно)
Библиотека, которая имеет тенденцию паниковать при определенных входных данных, является либо признаком ошибки, либо плохо спроектированного API. Учитывая, что рассматриваемая библиотека имеет открытый исходный код, не кажется надуманным направить вклад туда, где это необходимо. В противном случае вам понадобится дополнительный уровень очистки ввода. В крайнем случае есть catch_unwind. Тогда вторая часть вопроса, которая, на мой взгляд, более интересна, заключается в том, как превратить ошибку Rust в исключение C++.
Имейте в виду, что я никогда не использовал Rust, а как насчет doc.rust-lang.org/std/panic/fn.set_hook.html ? Это не вызовет исключения, но вы, вероятно, могли бы вызвать некоторую функцию в своем коде C++ с помощью "PanicInfo"... хотя я не уверен, можно ли вообще вернуть исходный кадр вызова, я бы попробовал бросьте изнутри эту функцию-перехватчик, просто чтобы посмотреть, что произойдет...
Rust объявляет, что выход паники за пределы FFI является неопределённым поведением. См. также stackoverflow.com/questions/77876748/…
Как отмечает Себастьян, вы не можете паниковать через границы FFI, FFI говорят на C (или на самом деле ABI платформы), и даже если бы они это сделали (Windows может поддерживать SEH), я не верю, что Rust использует это для паники. Итак, что вам следует делать, если вы не можете заставить восходящий поток исправить панику и не можете исправить свой собственный код, чтобы избежать ее запуска, это std::panic::catch_unwind в extern и вернуть безопасный код ошибки или структурированный ошибка.
Чтобы смешать Rust и C++, я бы установил panic = 'abort' в вашем Cargo.toml. Паника не является исключением.
Я только что бегло просмотрел исходный код jexl (docs.rs/jexl-parser/0.2.2/src/jexl_parser/lib.rs.html и docs.rs/jexl-eval/0.2.2 /src/jexl_eval/lib.rs.html ) и AFAICT паникует только в тестах. А публичные функции документированы как возвращающие Error, а не паникующие…
@Jmb спасибо за ваш отзыв! Вы правы: после тщательного изучения библиотека jexl-rs ведет себя правильно... это моя собственная библиотека-оболочка между jexl-rs и моей программой на C++, которой не было. Поэтому я изменю свою библиотеку, чтобы использовать шаблон Result<T, E>, принимая во внимание ценные отзывы, представленные в этом посте.





Для Rust нет C++ FFI, есть только C FFI.
В C нет исключений и, следовательно, нет специального ABI для распространения исключений.
В результате не существует стандартного механизма распространения исключений из Rust в C (или C в Rust), что не сильно поможет, поскольку в C все равно нет способа взаимодействовать с исключениями.
Здесь тоже нет никакой специфики Rust. Позволить исключениям C++ пересекать кадры стека C — это уже авантюра.
В любом случае кадры стека C не генерируют никакого кода для обработки раскрутки и, следовательно, не выполняют очистку при раскрутке во время распространения исключения, что приводит к неопределенному поведению. На практике последствия могут быть самыми разными: от «просто работает» до «протекающего» и «испорченного состояния».
Если инструментальная цепочка не гарантирует явную совместимость, исключения не должны пересекать границы языка.
Путем их перевода вручную или автоматически на границе FFI.
То есть от Rust к C++:
catch_unwind, чтобы перевести исключение в ошибку.А если вы забудете, надеюсь, RFC 2945 спасет вас (путем прерывания), когда вы испытываете.
От C++ к Rust:
catch, чтобы преобразовать исключение в ошибку.А если вы забудете, вы формально войдете в страну неопределенного поведения. Удачи.
«Я тестирую библиотеки Rust, и это может вызвать панику». Это абсолютно непреодолимо? Паники не предназначены для перехвата, как исключения C++. Самый близкий механизм исправления ошибок в Rust — это функции, возвращающие
Result<T, E>. Соответствующее чтение: stackoverflow.com/questions/30232890/… stackoverflow.com/questions/30824258/…