I have problems creating a ruby extension to export the C ++ library that I wrote in ruby under OSX. This simple example:
#include <boost/regex.hpp> extern "C" void Init_bayeux() { boost::regex expression("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"); }
a bad_cast exception is thrown:
#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, l=@0x7fff5fbfe520 ) at cpp_regex_traits.hpp:218 #4 0x0000000101138d42 in cpp_regex_traits_base (this=0x7fff5fbfe4d0, l=@0x7fff5fbfe520 ) at cpp_regex_traits.hpp:173 #5 0x000000010113eda6 in boost::re_detail::create_cpp_regex_traits<char> ( l=@0x7fff5fbfe520 ) 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
I will compile the extension with:
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
And finally, link the dynamic library to:
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 searched the Internet and tried all kinds of links and compilers. If I create an executable, there is no such problem. Has anyone else had such a problem and found a solution?
I studied this and found that the function that throws the exception is as follows:
std::locale loc = std::locale("C"); std::use_facet< std::collate<char> >( loc );
In the std :: collate <> source file, I found the throw status:
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 }
Does this make any difference to you?
Update: I tried Yang's suggestion:
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
:
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)
Update:
This is not a related check in use_facet () that throws, but the next line that actually performs dynamic selection. This example comes down to the fact that there might be something with RTTI:
#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 ); }
I used printf () because using cout also fails. Code output above:
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'
Update:
If I rename Init_bayeux to main () and associate it with the executable, the output will be the same, but the call will not end.
Update:
When I write a small program to load a shared library and to start Init_bayeux (), again, an exception is not thrown:
#include <dlfcn.h> int main() { void* handle = dlopen("bayeux.bundle", RTLD_LAZY|RTLD_GLOBAL ); void(*f)(void) = (void(*)(void)) dlsym( handle, "Init_bayeux" ) ; f(); }
So, it seems to me that this could be a problem with creating ruby.exe. It makes sense?
Update: I looked at addresses containing the names of two type_info objects. Same content but different addresses. I added the -flat_namespace parameter to the link. Now dynamic_cast works. The original problem with the regex boost library still exists, but I think this can be resolved by linking boost static in the shared library or by rebuilding the boost libraries using the -flat_namespace switch.
Update: Now I will return to the first example with the regex expression expression, create with this command:
g++ -shared -flat_namespace -fPIC -o bayeux.bundle /Users/todi/boost_1_49_0/stage/lib/libboost_regex.a bayeux.cpp
But when loading the extension into the ruby interpreter, static characters are not initialized:
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 ( k=@0x7fff5fbfddd0 ) 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 ...
Again, this will not work when I load the shared library with a small program from above.
Update: Now I am linking the first static example:
g++ -shared -fPIC -flat_namespace -nodefaultlibs -o bayeux.bundle -static -lstdc++ -lpthread -lgcc_eh -lboost_regex-mt bayeux.cpp
With the same error:
ruby(15197,0x7fff708aecc0) malloc: *** error for object 0x7fff7027e500: pointer being freed was not allocated
otool -L confirmed that each library is associated with static:
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)
debugging:
If I contact the boost debugging version, then it works as expected.