c++ - 无法创建 C++ ruby​​ 扩展

标签 c++ ruby macos boost ruby-c-extension

我在创建 ruby​​ 扩展以导出我在 OSX 下写入 ruby​​ 的 C++ 库时遇到问题。这个简单的例子:

#include <boost/regex.hpp>

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

导致抛出 bad_cast 异常:

#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

我用以下代码编译扩展:

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

最后链接动态库:

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

我已经在网上搜索并尝试了所有类型的链接和编译器开关。如果我构建可执行文件,则没有这样的问题。是否有人遇到过这样的问题并找到了解决方案?

我对此进行了进一步调查,发现导致异常的函数如下所示:

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

在 std::collat​​e<> 的源代码中,我找到了 throw 语句:

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
}

这对你来说有意义吗?

更新:我已经尝试了 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

版本:

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) 

更新:

抛出的不是 use_facet() 中的边界检查,而是下一行,它实际上进行了动态转换。这个例子将它归结为可能与 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 );
}

我使用了 printf() 因为 cout 的使用也失败了。上面代码的输出是:

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

构建:

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

更新:

如果我将 Init_bayeux 重命名为 main() 并将其链接到可执行文件,输出是相同的,但不会调用终止。

更新:

当我编写一个小程序来加载共享库并执行 Init_bayeux() 时,再次没有抛出异常:

#include <dlfcn.h>

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

所以在我看来,ruby.exe 的构建方式可能存在问题。这有意义吗?

更新: 我查看了包含两个 type_info 对象名称的地址。内容相同,但地址不同。我将 -flat_namespace 开关添加到链接命令。现在 dynamic_cast 起作用了。 boost regex 库的原始问题仍然存在,但我认为这可以通过将 boost 静态链接到共享库或通过使用 -flat_namespace 开关重建 boost 库来解决。

更新: 现在我回到第一个使用 boost 正则表达式的示例,使用此命令构建:

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

但是当将扩展加载到 ruby​​ 解释器中时,静态符号的初始化失败:

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
...

同样,当我通过上面的小 c 程序加载共享库时,这并没有失败。

更新: 现在我链接第一个静态示例:

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

同样的错误:

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

otool -L 确认每个库都是静态链接的:

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)

调试:

如果我链接到 boost 调试版本,那么它会像预期的那样工作。

最佳答案

备案:我现在已经使用完全相同的编译器(版本 4.2.1 [官方苹果版本])构建了 boost 和我的应用程序。到目前为止没有问题。为什么当 ruby​​ 扩展静态链接所有库时它不能按预期工作对我来说是一个奇迹。感谢所有花时间解决这个问题的人。

亲切的问候 托尔斯滕

关于c++ - 无法创建 C++ ruby​​ 扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11224096/

相关文章:

java - 无法从 Java 代码运行 PATH 中的程序

未调用 C++/CX D'tor

c++ - std::function 与 std::function 作为函数参数

c++ - STL map.find返回所有元素

Ruby TCP 聊天服务器

ruby - 通过网络套接字扩展多 channel 发布/订阅

swift - NSAttributedString 绘制位置在 10.13 和 10.14+ 上不同

ios - Xcode - 如何修复 'NSUnknownKeyException',原因 : … this class is not key value coding-compliant for the key X"error?

c++ - 在 C++ 中放置接口(interface)类的位置

ruby - 在 Ruby 中解析 JSON(如 XPATH)