librelist archives

« back to archive

Extending Ruby using C++ and Rice

Extending Ruby using C++ and Rice

From:
Lea Savage
Date:
2010-04-19 @ 09:36
Hi All,

I submitted a question to the following forum 
http://www.ruby-forum.com/topic/208124#905786 with regards to an issue I 
am experiencing with extending ruby with Rice.  I am not sure is this is a
Rice specific issue but I am taking the suggestion from         Jason 
Roelofs (see forum) to email this list.

My original post was:

Hi,

I am trying to extend Ruby and my current implementation is using C++ and 
Rice (http://rice.rubyforge.org/main.html).
Mac OS X 10.5.8
Ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]

I have completed the following steps as shown in Rice to create a cpp 
class (my_test.cpp)

#include "rice/Class.hpp"
#include "rice/Data_Type.hpp"
#include "rice/Constructor.hpp"
#include "MyTest.h"

using namespace Rice;

extern "C"
void Init_my_test()
{
    define_class<MyTest>("MyTest").
      define_constructor(Constructor<MyTest>()).
      define_method("read", &MyTest::read);
}

Here are my h and cpp files for MyTest

*** HEADER FILE ***
#ifndef __MyTest_h
#define __MyTest_h

#include <string>
#include "XternalDebug.hpp"

class MyTest
{
    public:
        MyTest() {};
        virtual ~MyTest(void){
            SX::Terminate();
            SXMP::Terminate();
        }
        std::string read(std::string filename);
};
#endif


*** CPP FILE ***
#include "MyTest.h"

extern "C" std::string MyTest::read(std::string filename)
{
    if(SXMP::Initialize())
    {
        // Must initialize SX before we use it
        if (SX::Initialize())
        {
            std::string status = "";
            bool ok;
            SX myFile;
            myFile.OpenFile(filename, UnknownFile, opts);
            std::string buffer;
            myFile.Get(&buffer);
            return buffer;
        }
    }
    return NULL;
}

SX and SXMP are taken from another library called libXternalDebug.a

My extconf.rb file looks as follows:
require 'rubygems'
require 'mkmf-rice'
$CPPFLAGS << ' -DMAC_ENV=1'
have_library("stdc++")
dir_config("xtoolkit")
have_library("XternalDebug")
create_makefile("my_test")

I pass the following arguments to extconf.rb
#ARCHFLAGS="-arch i386" ruby  extconf.rb 
--with-xtoolkit-include=/Users/<username>/xtoolkit/public/include 
--with-xtoolkit-lib=/Users/<username>/xtoolkit/public/libraries

$ ARCHFLAGS="-arch i386" ruby  extconf.rb 
--with-xtoolkit-include=/Users/<username>/xtoolkit/public/include 
--with-xtoolkit-lib=/Users/<username>/xtoolkit/public/libraries
checking for main() in -lrice... yes
checking for main() in -lstdc++... yes
checking for main() in -lX... yes
creating Makefile

I then run make:
$ makeg++ -I. -I. 
-I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0
-I. -I/Users/<username>/xtoolkit/public/include    
-I/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/include -DMAC_ENV=1 
-fno-common -arch i386 -Os -pipe -fno-common   -Wall -g -c my_test.cpp
g++ -I. -I. 
-I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0
-I. -I/Users/<username>/xtoolkit/public/include    
-I/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/include -DMAC_ENV=1 
-fno-common -arch i386 -Os -pipe -fno-common   -Wall -g -c MyTest.cpp
g++ -arch i386 -pipe -bundle -undefined dynamic_lookup -o my_test.bundle 
my_test.o MyTest.o -L. 
-L/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib 
-L/Users/<username>/xtoolkit/public/libraries -L. -arch i386  
-L/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/lib    -lXternalDebug 
-lstdc++ -lrice -lruby -lpthread -ldl -lm

(please note i have removed my username intentionally from the above paths)

I then have the following ruby test:
require 'my_test'
require 'test/unit'

class TestTest < Test::Unit::TestCase
   def test_test
    t = MyTest.new
    assert_equal(Object, MyTest.superclass)
    assert_equal(MyTest, t.class)
   end
end

When I try to run this I get the following error:
$ ruby testtest.rb
Loaded suite testtest
Started
.
Finished in 0.000476 seconds.

1 tests, 2 assertions, 0 failures, 0 errors
dyld: lazy symbol binding failed: Symbol not found: __ZN9TSXMISsE9TerminateEv
  Referenced from: /Users/<username>/tests/rice_test2/my_test.bundle
  Expected in: dynamic lookup

dyld: Symbol not found: __ZN9TSXISsE9TerminateEv
  Referenced from: /Users/<username>/tests/rice_test2/my_test.bundle
  Expected in: dynamic lookup

Trace/BPT trap

Does anyone know what I am doing wrong or what I am missing to get these 
errors?  Do I need to include this external library in a particular 
directory for ruby to discover?
Any help is much appreciated.

Lea

-- end of original post --

I have just tried his suggestion to remove extern C from my cpp class and 
I receive the following issue when running my test:

$ ruby testtest.rb
./my_test.bundle: dlsym(0x221a60, Init_my_test): symbol not found - 
./my_test.bundle (LoadError)
    from testtest.rb:1

I have also included the .a library in my directory to try and resolve the
load issues I am experiencing but still receiving the above issue.

Additionally I have tried to update the dyld_library_path:
DYLD_LIBRARY_PATH=/Users/<username>/xtoolkit/public/libraries:$DYLD_LIBRARY_PATH
export DYLD_LIBRARY_PATH

Does anyone know what I am doing wrong?

Thanks in advance,
Lea

Re: [rice] Extending Ruby using C++ and Rice

From:
Ammar Ali
Date:
2010-04-19 @ 10:12
On 4/19/10 12:36 PM, Lea Savage wrote:
> I have also included the .a library in my directory to try and resolve 
> the load issues I am experiencing but still receiving the above issue.
>
> Additionally I have tried to update the dyld_library_path:
> DYLD_LIBRARY_PATH=/Users/<username>/xtoolkit/public/libraries:$DYLD_LIBRARY_PATH
> export DYLD_LIBRARY_PATH

Archive library (.a) files do not get dynamically loaded, you need the 
.dyld version somewhere in the load path. If it doesn't exist, or you 
want to statically link it, try to include the full path to the .a file 
in the link step.

HTH,
Ammar