2012-06-27 15 views
6

Ho problemi a creare un'estensione ruby ​​per esportare una libreria C++ che ho scritto a ruby ​​sotto OSX. Questo semplice esempio:impossibile creare un'estensione rubino C++

#include <boost/regex.hpp> 

extern "C" void Init_bayeux() 
{ 
    boost::regex expression("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"); 
} 

determina un'eccezione bad_cast essere gettato:

#0 0x00000001014663bd in __cxa_throw() 
#1 0x00000001014cf6b2 in __cxa_bad_cast() 
#2 0x00000001014986f9 in std::use_facet<std::collate<char> >() 
#3 0x0000000101135a4f in boost::re_detail::cpp_regex_traits_base<char>::imbue (this=0x7fff5fbfe4d0, [email protected]) at cpp_regex_traits.hpp:218 
#4 0x0000000101138d42 in cpp_regex_traits_base (this=0x7fff5fbfe4d0, [email protected]) at cpp_regex_traits.hpp:173 
#5 0x000000010113eda6 in boost::re_detail::create_cpp_regex_traits<char> ([email protected]) at cpp_regex_traits.hpp:859 
#6 0x0000000101149bee in cpp_regex_traits (this=0x101600200) at cpp_regex_traits.hpp:880 
#7 0x0000000101142758 in regex_traits (this=0x101600200) at regex_traits.hpp:75 
#8 0x000000010113d68c in regex_traits_wrapper (this=0x101600200) at regex_traits.hpp:169 
#9 0x000000010113bae1 in regex_data (this=0x101600060) at basic_regex.hpp:166 
#10 0x000000010113981e in basic_regex_implementation (this=0x101600060) at basic_regex.hpp:202 
#11 0x0000000101136e1a in boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::do_assign (this=0x7fff5fbfe710, p1=0x100540ae0 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", p2=0x100540b19 "", f=0) at basic_regex.hpp:652 
#12 0x0000000100540a66 in boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::assign (this=0x7fff5fbfe710, p1=0x100540ae0 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", p2=0x100540b19 "", f=0) at basic_regex.hpp:379 
#13 0x0000000100540a13 in boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::assign (this=0x7fff5fbfe710, p=0x100540ae0 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", f=0) at basic_regex.hpp:364 
#14 0x000000010054096e in basic_regex (this=0x7fff5fbfe710, p=0x100540ae0 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", f=0) at basic_regex.hpp:333 
#15 0x00000001005407e2 in Init_bayeux() at bayeux.cpp:10 
#16 0x0000000100004593 in dln_load (file=0x1008bc000 "/Users/todi/sioux/lib/debug/rack/bayeux.bundle") at dln.c:1293 

compilo l'estensione con:

g++ ./source/rack/bayeux.cpp -o /Users/todi/sioux/obj/debug/rack/bayeux.o -Wall -pedantic -Wno-parentheses -Wno-sign-compare -fno-common -c -pipe -I/Users/todi/sioux/source -ggdb -O0 

E infine a collegare la libreria dinamica con:

g++ -o /Users/todi/sioux/lib/debug/rack/bayeux.bundle -bundle -ggdb /Users/todi/sioux/obj/debug/rack/bayeux.o -L/Users/todi/sioux/lib/debug -lrack -lboost_regex-mt-d -lruby 

I s s Ricercato il web e provato tutti i tipi di switch di link e compilatori. Se costruisco un eseguibile non esiste un problema del genere. Qualcun altro ha avuto un tale problema e ha trovato una soluzione?

ho esaminato più approfonditamente la e ha scoperto che la funzione che causa l'eccezione si presenta così:

std::locale loc = std::locale("C"); 
std::use_facet< std::collate<char> >(loc); 

nella fonte di std :: raccogliere <> Ho trovato la statment tiro:

use_facet(const locale& __loc) 
{ 
    const size_t __i = _Facet::id._M_id(); 
    const locale::facet** __facets = __loc._M_impl->_M_facets; 
    if (__i >= __loc._M_impl->_M_facets_size || !__facets[__i]) 
    __throw_bad_cast(); 
#ifdef __GXX_RTTI 
     return dynamic_cast<const _Facet&>(*__facets[__i]); 
#else 
     return static_cast<const _Facet&>(*__facets[__i]); 
#endif 
} 

Ha senso per te?

Aggiornamento: Ho provato il suggerimento di Jan:

Todis-MacBook-Pro:rack todi$ g++ -shared -fpic -o bayeux.bundle bayeux.cpp 
Todis-MacBook-Pro:rack todi$ ruby -I. -rbayeux -e 'puts :ok' 
terminate called after throwing an instance of 'std::bad_cast' 
    what(): std::bad_cast 
Abort trap 

versioni:

Todis-MacBook-Pro:rack todi$ ruby -v 
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0] 
Todis-MacBook-Pro:rack todi$ gcc -v 
Using built-in specs. 
COLLECT_GCC=gcc 
COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/x86_64-apple-darwin10/4.5.2/lto-wrapper 
Target: x86_64-apple-darwin10 
Configured with: ../gcc-4.5.2/configure --prefix=/opt/local --build=x86_64-apple-darwin10 --enable-languages=c,c++,objc,obj-c++,fortran,java --libdir=/opt/local/lib/gcc45 --includedir=/opt/local/include/gcc45 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/gcc-4.5 --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-4.5 --with-gxx-include-dir=/opt/local/include/gcc45/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --enable-stage1-checking --disable-multilib --enable-fully-dynamic-string 
Thread model: posix 
gcc version 4.5.2 (GCC) 

Aggiornamento:

Non è la-check rilegato in use_facet() che lancia, ma il linea successiva, che in realtà fa un cast dinamico. Questo esempio bolle giù a forse qualcosa con RTTI: printf

#define private public 
#include <locale> 
#include <iostream> 
#include <typeinfo> 

extern "C" void Init_bayeux() 
{ 
    std::locale loc = std::locale("C"); 
    printf("size: %i\n", loc._M_impl->_M_facets_size); 
    printf("id: %i\n", std::collate<char>::id._M_id()); 

    const std::locale::facet& fac = *loc._M_impl->_M_facets[ std::collate<char>::id._M_id() ]; 

    printf("name: %s\n", typeid(fac).name()); 
    printf("name: %s\n", typeid(std::collate<char>).name()); 

    const std::type_info& a = typeid(fac); 
    const std::type_info& b = typeid(std::collate<char>); 

    printf("equal: %i\n", !a.before(b) && !b.before(a)); 
    dynamic_cast< const std::collate<char>& >(fac); 
} 

Ho usato() in quanto l'utilizzo di cout riesce anche. L'uscita del codice di cui sopra è:

size: 28 
id: 5 
name: St7collateIcE 
name: St7collateIcE 
equal: 1 
terminate called after throwing an instance of 'std::bad_cast' 
    what(): std::bad_cast 
Abort trap 

Build with:

g++ -shared -fpic -o bayeux.bundle bayeux.cpp && ruby -I. -rbayeux -e 'puts :ok' 

Aggiornamento:

Se cambio titolo Init_bayeux a main() e collegarlo a un eseguibile, l'uscita è la lo stesso, ma nessuna chiamata per terminare.

Aggiornamento:

Quando scrivo un piccolo programma per caricare la libreria condivisa ed eseguire Init_bayeux(), ancora una volta, si butta non fa eccezione:

#include <dlfcn.h> 

int main() 
{ 
    void* handle = dlopen("bayeux.bundle", RTLD_LAZY|RTLD_GLOBAL); 
    void(*f)(void) = (void(*)(void)) dlsym(handle, "Init_bayeux") ; 
    f(); 
} 

Così sembra a me, che potrebbe essere un problema con come è stato creato ruby.exe. Ha senso?

Aggiornamento: Ho dato un'occhiata agli indirizzi contenenti i nomi dei due oggetti type_info. Lo stesso contenuto, ma indirizzi diversi.Ho aggiunto l'opzione -flat_namespace al comando link. Ora il dynamic_cast funziona. Il problema originale con la libreria regex di boost esiste ancora, ma penso che ciò potrebbe essere risolvibile collegando boost in modo statico nella libreria condivisa o ricostruendo le librerie di boost con lo switch -flat_namespace.

Update: ora sono tornato al primo esempio con l'espressione spinta regex, costruire con questo comando:

g++ -shared -flat_namespace -fPIC -o bayeux.bundle /Users/todi/boost_1_49_0/stage/lib/libboost_regex.a bayeux.cpp 

Ma quando si carica l'estensione nell'interprete rubino, l'inizializzazione di simboli statiche fallisce :

ruby(59384,0x7fff712b8cc0) malloc: *** error for object 0x7fff70b19500: pointer being freed was not allocated 
*** set a breakpoint in malloc_error_break to debug 

Program received signal SIGABRT, Aborted. 
0x00007fff8a6ab0b6 in __kill() 
(gdb) bt 
#0 0x00007fff8a6ab0b6 in __kill() 
#1 0x00007fff8a74b9f6 in abort() 
#2 0x00007fff8a663195 in free() 
#3 0x0000000100541023 in boost::re_detail::cpp_regex_traits_char_layer<char>::init (this=0x10060be50) at basic_string.h:237 
#4 0x0000000100543904 in boost::object_cache<boost::re_detail::cpp_regex_traits_base<char>, boost::re_detail::cpp_regex_traits_implementation<char> >::do_get ([email protected]) at cpp_regex_traits.hpp:366 
#5 0x000000010056005b in create_cpp_regex_traits<char> (l=<value temporarily unavailable, due to optimizations>) at pending/object_cache.hpp:69 
#6 0x0000000100544c33 in boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::do_assign (this=0x7fff5fbfe090, p1=0x100567158 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", p2=0x100567191 "", f=0) at cpp_regex_traits.hpp:880 
#7 0x0000000100566280 in boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::assign() 
#8 0x000000010056622d in boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::assign() 
#9 0x0000000100566188 in boost::basic_regex<char, boost::regex_traits<char, boost::cpp_regex_traits<char> > >::basic_regex() 
#10 0x0000000100566025 in Init_bayeux() 
#11 0x0000000100003a23 in dln_load (file=0x10201a000 "/Users/todi/sioux/source/rack/bayeux.bundle") at dln.c:1293 
#12 0x000000010016569d in vm_pop_frame [inlined]() at /Users/todi/.rvm/src/ruby-1.9.2-p320/vm_insnhelper.c:1465 
#13 0x000000010016569d in rb_vm_call_cfunc (recv=4303980440, func=0x100042520 <load_ext>, arg=4303803000, blockptr=0x1, filename=<value temporarily unavailable, due to optimizations>, filepath=<value temporarily unavailable, due to optimizations>) at vm.c:1467 
#14 0x0000000100043382 in rb_require_safe (fname=4303904640, safe=0) at load.c:602 
#15 0x000000010017cbf3 in vm_call_cfunc [inlined]() at /Users/todi/.rvm/src/ruby-1.9.2-p320/vm_insnhelper.c:402 
#16 0x000000010017cbf3 in vm_call_method (th=0x1003016b0, cfp=0x1004ffef8, num=1, blockptr=0x1, flag=8, id=<value temporarily unavailable, due to optimizations>, me=0x10182cfa0, recv=4303980440) at vm_insnhelper.c:528 
... 

di nuovo, questo non manca, quando carico la libreria condivisa dal piccolo programma c dall'alto.

Update: Ora i Link il primo esempio statica:

g++ -shared -fPIC -flat_namespace -nodefaultlibs -o bayeux.bundle -static -lstdc++ -lpthread -lgcc_eh -lboost_regex-mt bayeux.cpp 

Con lo stesso errore:

ruby(15197,0x7fff708aecc0) malloc: *** error for object 0x7fff7027e500: pointer being freed was not allocated 

otool -L confermato che ogni biblioteca è collegata statica:

bayeux.bundle: 
bayeux.bundle (compatibility version 0.0.0, current version 0.0.0) 
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11) 

debug:

Se eseguo il collegamento con la versione di debug boost, funziona come previsto.

+4

non riesco a riprodurre il comportamento che hai descritto. Ho compilato il file C++ di esempio con 'g ++ -shared -fpic -o bayeux.so -lboost_regex bayeux.cpp' e' ruby ​​-I. -rbayeux -e 'puts: ok'' ha caricato l'estensione senza problemi. Tuttavia, ho avuto alcuni problemi quando ho omesso '-lboost_regex'. Inoltre, potresti specificare quali versioni di Ruby e Boost stai usando e come possiamo riprodurre questo problema? – Jan

+0

@ Jan, grazie per averci provato. Su OS X, devo cambiare l'estensione "so" in "bundle" (altrimenti il ​​require non troverà la libreria) ma il risultato è sempre lo stesso. –

+0

@Jan, puoi omettere la libreria boost, fallisce senza di essa. È la chiamata a use_facet <> che non riesce. –

risposta

0

Per i record: ora ho sviluppato boost e la mia applicazione con lo stesso compilatore (versione 4.2.1 [versione ufficiale di apple]). Nessun problema finora. Perché non funzionerà come previsto quando l'estensione ruby ​​collega tutte le librerie in modo statico è un miracolo per me. Grazie a tutti coloro che hanno dedicato tempo a questo problema.

Cordiali saluti Torsten

Problemi correlati