Buduję wspólną bibliotekę z f-no-rtti
. Wewnętrznie ta biblioteka wyrzuca std:invalid_argument
i przechwytuje std::exception
, ale klauzula catch
nigdy nie jest wprowadzana.Polimorficzne wychwytywanie wyjątku w bibliotece współdzielonej -fno-rtti na Mac OS X
Poniższy kod reprodukuje problemu (g ++ 4.2, Mac OS X 10.6):
// library.cpp: exports f(), compiled with -fno-rtti
#include <stdexcept>
#include <iostream>
extern "C" {
void f() {
try {
throw std::invalid_argument("std::exception handler");
} catch(std::exception& e) {
std::cout << e.what() << "\n";
} catch(...) {
std::cout << "... handler\n";
}
}
}
// main.cpp: the main executable, dynamically loads the library
#include <dlfcn.h>
typedef void(*fPtr)();
int main() {
void* handle = dlopen("./libexception_problem.dylib", RTLD_LAZY);
fPtr p_f = reinterpret_cast<fPtr>(dlsym(handle, "f"));
p_f();
}
wyjściowa:
MacBook-Pro:teste pfranco$ # works fine with rtti
MacBook-Pro:teste pfranco$ g++ -c library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ main.cpp -o main && ./main
std::exception handler
MacBook-Pro:teste pfranco$ # breaks with -fno-rtti
MacBook-Pro:teste pfranco$ g++ -c -fno-rtti library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ -fno-rtti main.cpp -o main && ./main
... handler
MacBook-Pro:teste pfranco$ #-no_dead_strip_inits_and_terms doesn't change anything
MacBook-Pro:teste pfranco$ g++ -c -no_dead_strip_inits_and_terms -fno-rtti library.cpp && g++ -no_dead_strip_inits_and_terms -shared -o libexception_problem.dylib library.o && g++ -fno-rtti -no_dead_strip_inits_and_terms main.cpp -o main && ./main
... handler
MacBook-Pro:teste pfranco$ # linking against the shared library works, but this isn't always an option
MacBook-Pro:teste pfranco$ g++ -c -fno-rtti library.cpp && g++ -shared -o libexception_problem.dylib library.o && g++ -fno-rtti main.cpp -o main -L. -lexception_problem && ./main
std::exception handler
ten dzieje się tak tylko wtedy, gdy kod, który wyrzuca, znajduje się w udostępnionej bibliotece , i tylko wtedy, gdy złapany typ jest podstawową klasą rzeczywistego wyjątku - catch(std::invalid_argument&)
działa dobrze, std::logic_error&
nie działa.
Co ciekawe, nie dzieje się to w Linuksie, nawet podczas wykonywania dokładnie tych samych poleceń.
Pytania:
- Dlaczego tak się dzieje? Czy jest to błąd, niezdefiniowane zachowanie lub projekt?
- Jak mogę sprawić, by działało, bez łączenia z biblioteką?
Wielkie dzięki.
Jeśli przez "niezdefiniowane zachowanie" rozumie się zgodnie ze standardem C++, to jest to (w najlepszym przypadku) zdefiniowane przez implementację, co się dzieje, gdy używa się opcji kompilatora, która umieszcza kompilator w trybie niezgodnym.Wątpię, że to ci bardzo pomaga, ale nie możesz wyłączyć bitów standardu, a potem oczekiwać, że standard ci pomoże ;-) –